From c38deee8076f300e29214648108eddf33db3e200 Mon Sep 17 00:00:00 2001 From: Paul Robinson Date: Mon, 24 Nov 2014 18:05:29 +0000 Subject: [PATCH] More long path name support on Windows, this time in program execution. Allows long paths for the executable and redirected stdin/stdout/stderr. Addresses PR21563. llvm-svn: 222671 --- llvm/lib/Support/Windows/Path.inc | 12 +++++--- llvm/lib/Support/Windows/Program.inc | 15 ++++++---- llvm/lib/Support/Windows/WindowsSupport.h | 6 ++++ llvm/unittests/Support/ProgramTest.cpp | 50 +++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 9 deletions(-) diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc index 365031c..adb9a60 100644 --- a/llvm/lib/Support/Windows/Path.inc +++ b/llvm/lib/Support/Windows/Path.inc @@ -44,6 +44,7 @@ using namespace llvm; using llvm::sys::windows::UTF8ToUTF16; using llvm::sys::windows::UTF16ToUTF8; +using llvm::sys::path::widenPath; static std::error_code windows_error(DWORD E) { return mapWindowsError(E); @@ -59,11 +60,15 @@ static bool is_separator(const wchar_t value) { } } +namespace llvm { +namespace sys { +namespace path { + // Convert a UTF-8 path to UTF-16. Also, if the absolute equivalent of the // path is longer than CreateDirectory can tolerate, make it absolute and // prefixed by '\\?\'. -static std::error_code widenPath(const Twine &Path8, - SmallVectorImpl &Path16) { +std::error_code widenPath(const Twine &Path8, + SmallVectorImpl &Path16) { const size_t MaxDirLen = MAX_PATH - 12; // Must leave room for 8.3 filename. // Several operations would convert Path8 to SmallString; more efficient to @@ -111,9 +116,8 @@ static std::error_code widenPath(const Twine &Path8, // Just use the caller's original path. return UTF8ToUTF16(Path8Str, Path16); } +} // end namespace path -namespace llvm { -namespace sys { namespace fs { std::string getMainExecutable(const char *argv0, void *MainExecAddr) { diff --git a/llvm/lib/Support/Windows/Program.inc b/llvm/lib/Support/Windows/Program.inc index 72c2a58..6467ddd 100644 --- a/llvm/lib/Support/Windows/Program.inc +++ b/llvm/lib/Support/Windows/Program.inc @@ -117,14 +117,19 @@ static HANDLE RedirectIO(const StringRef *path, int fd, std::string* ErrMsg) { sa.bInheritHandle = TRUE; SmallVector fnameUnicode; - if (windows::UTF8ToUTF16(fname, fnameUnicode)) - return INVALID_HANDLE_VALUE; - + if (path->empty()) { + // Don't play long-path tricks on "NUL". + if (windows::UTF8ToUTF16(fname, fnameUnicode)) + return INVALID_HANDLE_VALUE; + } else { + if (path::widenPath(fname, fnameUnicode)) + return INVALID_HANDLE_VALUE; + } h = CreateFileW(fnameUnicode.data(), fd ? GENERIC_WRITE : GENERIC_READ, FILE_SHARE_READ, &sa, fd == 0 ? OPEN_EXISTING : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) { - MakeErrMsg(ErrMsg, std::string(fname) + ": Can't open file for " + + MakeErrMsg(ErrMsg, fname + ": Can't open file for " + (fd ? "input: " : "output: ")); } @@ -322,7 +327,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **args, fflush(stderr); SmallVector ProgramUtf16; - if (std::error_code ec = windows::UTF8ToUTF16(Program, ProgramUtf16)) { + if (std::error_code ec = path::widenPath(Program, ProgramUtf16)) { SetLastError(ec.value()); MakeErrMsg(ErrMsg, std::string("Unable to convert application name to UTF-16")); diff --git a/llvm/lib/Support/Windows/WindowsSupport.h b/llvm/lib/Support/Windows/WindowsSupport.h index 6d9c5fb..1d1cedc 100644 --- a/llvm/lib/Support/Windows/WindowsSupport.h +++ b/llvm/lib/Support/Windows/WindowsSupport.h @@ -30,6 +30,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" #include "llvm/Config/config.h" // Get build system configuration settings #include "llvm/Support/Compiler.h" #include @@ -162,6 +163,11 @@ c_str(SmallVectorImpl &str) { } namespace sys { +namespace path { +std::error_code widenPath(const Twine &Path8, + SmallVectorImpl &Path16); +} // end namespace path + namespace windows { std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl &utf16); std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, diff --git a/llvm/unittests/Support/ProgramTest.cpp b/llvm/unittests/Support/ProgramTest.cpp index c0e6e80..0feed47 100644 --- a/llvm/unittests/Support/ProgramTest.cpp +++ b/llvm/unittests/Support/ProgramTest.cpp @@ -70,6 +70,56 @@ static void CopyEnvironment(std::vector &out) { } } +#ifdef LLVM_ON_WIN32 +TEST(ProgramTest, CreateProcessLongPath) { + if (getenv("LLVM_PROGRAM_TEST_LONG_PATH")) + exit(0); + + // getMainExecutable returns an absolute path; prepend the long-path prefix. + std::string MyAbsExe = + sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1); + std::string MyExe; + if (!StringRef(MyAbsExe).startswith("\\\\?\\")) + MyExe.append("\\\\?\\"); + MyExe.append(MyAbsExe); + + const char *ArgV[] = { + MyExe.c_str(), + "--gtest_filter=ProgramTest.CreateProcessLongPath", + nullptr + }; + + // Add LLVM_PROGRAM_TEST_LONG_PATH to the environment of the child. + std::vector EnvP; + CopyEnvironment(EnvP); + EnvP.push_back("LLVM_PROGRAM_TEST_LONG_PATH=1"); + EnvP.push_back(nullptr); + + // Redirect stdout to a long path. + SmallString<128> TestDirectory; + ASSERT_NO_ERROR( + fs::createUniqueDirectory("program-redirect-test", TestDirectory)); + SmallString<256> LongPath(TestDirectory); + LongPath.push_back('\\'); + // MAX_PATH = 260 + LongPath.append(260 - TestDirectory.size(), 'a'); + StringRef LongPathRef(LongPath); + + std::string Error; + bool ExecutionFailed; + const StringRef *Redirects[] = { nullptr, &LongPathRef, nullptr }; + int RC = ExecuteAndWait(MyExe, ArgV, &EnvP[0], Redirects, + /*secondsToWait=*/ 10, /*memoryLimit=*/ 0, &Error, + &ExecutionFailed); + EXPECT_FALSE(ExecutionFailed) << Error; + EXPECT_EQ(0, RC); + + // Remove the long stdout. + ASSERT_NO_ERROR(fs::remove(Twine(LongPath))); + ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory))); +} +#endif + TEST(ProgramTest, CreateProcessTrailingSlash) { if (getenv("LLVM_PROGRAM_TEST_CHILD")) { if (ProgramTestStringArg1 == "has\\\\ trailing\\" && -- 2.7.4