[llvm-commits] [llvm] r120776 - in /llvm/trunk: include/llvm/Support/FileSystem.h lib/Support/Unix/PathV2.inc lib/Support/Windows/PathV2.inc lib/Support/Windows/system_error.inc unittests/Support/Path.cpp
Michael J. Spencer
bigcheesegs at gmail.com
Thu Dec 2 17:21:28 PST 2010
Author: mspencer
Date: Thu Dec 2 19:21:28 2010
New Revision: 120776
URL: http://llvm.org/viewvc/llvm-project?rev=120776&view=rev
Log:
Support/FileSystem: Add unique_file and exists implementations.
Modified:
llvm/trunk/include/llvm/Support/FileSystem.h
llvm/trunk/lib/Support/Unix/PathV2.inc
llvm/trunk/lib/Support/Windows/PathV2.inc
llvm/trunk/lib/Support/Windows/system_error.inc
llvm/trunk/unittests/Support/Path.cpp
Modified: llvm/trunk/include/llvm/Support/FileSystem.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/FileSystem.h?rev=120776&r1=120775&r2=120776&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/FileSystem.h (original)
+++ llvm/trunk/include/llvm/Support/FileSystem.h Thu Dec 2 19:21:28 2010
@@ -390,19 +390,21 @@
///
/// Generates a unique path suitable for a temporary file and then opens it as a
/// file. The name is based on \a model with '%' replaced by a random char in
-/// [0-9a-f].
+/// [0-9a-f]. If \a model is not an absolute path, a suitable temporary
+/// directory will be prepended.
///
/// This is an atomic operation. Either the file is created and opened, or the
/// file system is left untouched.
///
-/// clang-%%-%%-%%-%%-%%.s => <current-directory>/clang-a0-b1-c2-d3-e4.s
+/// clang-%%-%%-%%-%%-%%.s => /tmp/clang-a0-b1-c2-d3-e4.s
///
/// @param model Name to base unique path off of.
-/// @param result Set to the opened file.
-/// @results errc::success if result has been successfully set, otherwise a
-/// platform specific error_code.
-/// @see temp_directory_path
-error_code unique_file(const Twine &model, void* i_have_not_decided_the_ty_yet);
+/// @param result_fs Set to the opened file's file descriptor.
+/// @param result_path Set to the opened file's absolute path.
+/// @results errc::success if result_{fd,path} have been successfully set,
+/// otherwise a platform specific error_code.
+error_code unique_file(const Twine &model, int &result_fd,
+ SmallVectorImpl<char> &result_path);
/// @brief Canonicalize path.
///
Modified: llvm/trunk/lib/Support/Unix/PathV2.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Unix/PathV2.inc?rev=120776&r1=120775&r2=120776&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Unix/PathV2.inc (original)
+++ llvm/trunk/lib/Support/Unix/PathV2.inc Thu Dec 2 19:21:28 2010
@@ -23,10 +23,12 @@
#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
+#if HAVE_STDIO_H
+#include <stdio.h>
#endif
+using namespace llvm;
+
namespace {
struct AutoFD {
int FileDescriptor;
@@ -45,6 +47,24 @@
operator int() const {return FileDescriptor;}
};
+
+ error_code TempDir(SmallVectorImpl<char> &result) {
+ // FIXME: Don't use TMPDIR if program is SUID or SGID enabled.
+ const char *dir = 0;
+ (dir = std::getenv("TMPDIR" )) ||
+ (dir = std::getenv("TMP" )) ||
+ (dir = std::getenv("TEMP" )) ||
+ (dir = std::getenv("TEMPDIR")) ||
+#ifdef P_tmpdir
+ (dir = P_tmpdir) ||
+#endif
+ (dir = "/tmp");
+
+ result.set_size(0);
+ StringRef d(dir);
+ result.append(d.begin(), d.end());
+ return make_error_code(errc::success);
+ }
}
namespace llvm {
@@ -126,6 +146,113 @@
return make_error_code(errc::success);
}
+error_code exists(const Twine &path, bool &result) {
+ SmallString<128> path_storage;
+ StringRef p = path.toNullTerminatedStringRef(path_storage);
+
+ struct stat status;
+ if (::stat(p.begin(), &status) == -1) {
+ if (errno != ENOENT)
+ return error_code(errno, system_category());
+ 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) {
+ SmallString<128> Model;
+ model.toVector(Model);
+ // Null terminate.
+ Model.c_str();
+
+ // Make model absolute by prepending a temp directory if it's not already.
+ bool absolute;
+ if (error_code ec = path::is_absolute(Twine(Model), absolute)) return ec;
+ if (!absolute) {
+ SmallString<128> TDir;
+ if (error_code ec = TempDir(TDir)) return ec;
+ if (error_code ec = path::append(TDir, Twine(Model))) return ec;
+ Model.swap(TDir);
+ }
+
+ // Replace '%' with random chars. From here on, DO NOT modify model. It may be
+ // needed if the randomly chosen path already exists.
+ SmallString<128> RandomPath;
+ RandomPath.reserve(Model.size() + 1);
+ ::srand(::time(NULL));
+
+retry_random_path:
+ // This is opened here instead of above to make it easier to track when to
+ // close it. Collisions should be rare enough for the possible extra syscalls
+ // not to matter.
+ FILE *RandomSource = ::fopen("/dev/urandom", "r");
+ RandomPath.set_size(0);
+ for (SmallVectorImpl<char>::const_iterator i = Model.begin(),
+ e = Model.end(); i != e; ++i) {
+ if (*i == '%') {
+ char val = 0;
+ if (RandomSource)
+ val = fgetc(RandomSource);
+ else
+ val = ::rand();
+ RandomPath.push_back("0123456789abcdef"[val & 15]);
+ } else
+ RandomPath.push_back(*i);
+ }
+
+ if (RandomSource)
+ ::fclose(RandomSource);
+
+ // Try to open + create the file.
+rety_open_create:
+ int RandomFD = ::open(RandomPath.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (RandomFD == -1) {
+ // If the file existed, try again, otherwise, error.
+ if (errno == EEXIST)
+ goto retry_random_path;
+ // The path prefix doesn't exist.
+ if (errno == ENOENT) {
+ StringRef p(RandomPath.begin(), RandomPath.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) {
+ // Don't try to create network paths.
+ if (i->size() > 2 && (*i)[0] == '/' &&
+ (*i)[1] == '/' &&
+ (*i)[2] != '/')
+ return error_code(ENOENT, system_category());
+ if (::mkdir(dir_to_create.c_str(), 0700) == -1)
+ return error_code(errno, system_category());
+ }
+ }
+ goto rety_open_create;
+ }
+ return error_code(errno, system_category());
+ }
+
+ // Make the path absolute.
+ char real_path_buff[PATH_MAX + 1];
+ if (realpath(RandomPath.c_str(), real_path_buff) == NULL) {
+ ::close(RandomFD);
+ ::unlink(RandomPath.c_str());
+ return error_code(errno, system_category());
+ }
+
+ result_path.set_size(0);
+ StringRef d(real_path_buff);
+ result_path.append(d.begin(), d.end());
+
+ result_fd = RandomFD;
+ return make_error_code(errc::success);
+}
+
} // end namespace fs
} // end namespace sys
} // end namespace llvm
Modified: llvm/trunk/lib/Support/Windows/PathV2.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Windows/PathV2.inc?rev=120776&r1=120775&r2=120776&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Windows/PathV2.inc (original)
+++ llvm/trunk/lib/Support/Windows/PathV2.inc Thu Dec 2 19:21:28 2010
@@ -17,6 +17,8 @@
//===----------------------------------------------------------------------===//
#include "Windows.h"
+#include <WinCrypt.h>
+#include <io.h>
using namespace llvm;
@@ -46,6 +48,62 @@
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 @@
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
Modified: llvm/trunk/lib/Support/Windows/system_error.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Windows/system_error.inc?rev=120776&r1=120775&r2=120776&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Windows/system_error.inc (original)
+++ llvm/trunk/lib/Support/Windows/system_error.inc Thu Dec 2 19:21:28 2010
@@ -96,6 +96,7 @@
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);
Modified: llvm/trunk/unittests/Support/Path.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/Path.cpp?rev=120776&r1=120775&r2=120776&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/Path.cpp (original)
+++ llvm/trunk/unittests/Support/Path.cpp Thu Dec 2 19:21:28 2010
@@ -7,11 +7,13 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/PathV2.h"
#include "gtest/gtest.h"
using namespace llvm;
+using namespace llvm::sys;
#define TEST_OUT(func, result) outs() << " " #func ": " << result << '\n';
@@ -131,6 +133,22 @@
outs().flush();
}
+
+ int FileDescriptor;
+ SmallString<64> TempPath;
+ if (error_code ec = sys::fs::unique_file("%%-%%-%%-%%.temp",
+ FileDescriptor, TempPath))
+ ASSERT_FALSE(ec.message().c_str());
+
+ bool TempFileExists;
+ ASSERT_FALSE(sys::fs::exists(Twine(TempPath), TempFileExists));
+ EXPECT_TRUE(TempFileExists);
+
+ ::close(FileDescriptor);
+ ::remove(TempPath.begin());
+
+ ASSERT_FALSE(fs::exists(Twine(TempPath), TempFileExists));
+ EXPECT_FALSE(TempFileExists);
}
} // anonymous namespace
More information about the llvm-commits
mailing list