mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-02-03 00:33:09 +00:00
Improve long path name support on Windows.
Windows normally limits the length of an absolute path name to 260 characters; directories can have lower limits. These limits increase to about 32K if you use absolute paths with the special '\\?\' prefix. Teach Support\Windows\Path.inc to use that prefix as needed. TODO: Other parts of Support could also learn to use this prefix. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@221841 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
2217648f91
commit
038e20451d
@ -59,6 +59,59 @@ static bool is_separator(const wchar_t value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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<wchar_t> &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
|
||||||
|
// do it once up front.
|
||||||
|
SmallString<128> Path8Str;
|
||||||
|
Path8.toVector(Path8Str);
|
||||||
|
|
||||||
|
// If we made this path absolute, how much longer would it get?
|
||||||
|
size_t CurPathLen;
|
||||||
|
if (llvm::sys::path::is_absolute(Twine(Path8Str)))
|
||||||
|
CurPathLen = 0; // No contribution from current_path needed.
|
||||||
|
else {
|
||||||
|
CurPathLen = ::GetCurrentDirectoryW(0, NULL);
|
||||||
|
if (CurPathLen == 0)
|
||||||
|
return windows_error(::GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Would the absolute path be longer than our limit?
|
||||||
|
if ((Path8Str.size() + CurPathLen) >= MaxDirLen &&
|
||||||
|
!Path8Str.startswith("\\\\?\\")) {
|
||||||
|
SmallString<2*MAX_PATH> FullPath("\\\\?\\");
|
||||||
|
if (CurPathLen) {
|
||||||
|
SmallString<80> CurPath;
|
||||||
|
if (std::error_code EC = llvm::sys::fs::current_path(CurPath))
|
||||||
|
return EC;
|
||||||
|
FullPath.append(CurPath);
|
||||||
|
}
|
||||||
|
// Traverse the requested path, canonicalizing . and .. as we go (because
|
||||||
|
// the \\?\ prefix is documented to treat them as real components).
|
||||||
|
// The iterators don't report separators and append() always attaches
|
||||||
|
// preferred_separator so we don't need to call native() on the result.
|
||||||
|
for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(Path8Str),
|
||||||
|
E = llvm::sys::path::end(Path8Str);
|
||||||
|
I != E; ++I) {
|
||||||
|
if (I->size() == 1 && *I == ".")
|
||||||
|
continue;
|
||||||
|
if (I->size() == 2 && *I == "..")
|
||||||
|
llvm::sys::path::remove_filename(FullPath);
|
||||||
|
else
|
||||||
|
llvm::sys::path::append(FullPath, *I);
|
||||||
|
}
|
||||||
|
return UTF8ToUTF16(FullPath, Path16);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just use the caller's original path.
|
||||||
|
return UTF8ToUTF16(Path8Str, Path16);
|
||||||
|
}
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
namespace sys {
|
namespace sys {
|
||||||
namespace fs {
|
namespace fs {
|
||||||
@ -130,11 +183,9 @@ std::error_code current_path(SmallVectorImpl<char> &result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::error_code create_directory(const Twine &path, bool IgnoreExisting) {
|
std::error_code create_directory(const Twine &path, bool IgnoreExisting) {
|
||||||
SmallString<128> path_storage;
|
|
||||||
SmallVector<wchar_t, 128> path_utf16;
|
SmallVector<wchar_t, 128> path_utf16;
|
||||||
|
|
||||||
if (std::error_code ec =
|
if (std::error_code ec = widenPath(path, path_utf16))
|
||||||
UTF8ToUTF16(path.toStringRef(path_storage), path_utf16))
|
|
||||||
return ec;
|
return ec;
|
||||||
|
|
||||||
if (!::CreateDirectoryW(path_utf16.begin(), NULL)) {
|
if (!::CreateDirectoryW(path_utf16.begin(), NULL)) {
|
||||||
@ -148,18 +199,12 @@ std::error_code create_directory(const Twine &path, bool IgnoreExisting) {
|
|||||||
|
|
||||||
// We can't use symbolic links for windows.
|
// We can't use symbolic links for windows.
|
||||||
std::error_code create_link(const Twine &to, const Twine &from) {
|
std::error_code create_link(const Twine &to, const Twine &from) {
|
||||||
// Get arguments.
|
|
||||||
SmallString<128> from_storage;
|
|
||||||
SmallString<128> to_storage;
|
|
||||||
StringRef f = from.toStringRef(from_storage);
|
|
||||||
StringRef t = to.toStringRef(to_storage);
|
|
||||||
|
|
||||||
// Convert to utf-16.
|
// Convert to utf-16.
|
||||||
SmallVector<wchar_t, 128> wide_from;
|
SmallVector<wchar_t, 128> wide_from;
|
||||||
SmallVector<wchar_t, 128> wide_to;
|
SmallVector<wchar_t, 128> wide_to;
|
||||||
if (std::error_code ec = UTF8ToUTF16(f, wide_from))
|
if (std::error_code ec = widenPath(from, wide_from))
|
||||||
return ec;
|
return ec;
|
||||||
if (std::error_code ec = UTF8ToUTF16(t, wide_to))
|
if (std::error_code ec = widenPath(to, wide_to))
|
||||||
return ec;
|
return ec;
|
||||||
|
|
||||||
if (!::CreateHardLinkW(wide_from.begin(), wide_to.begin(), NULL))
|
if (!::CreateHardLinkW(wide_from.begin(), wide_to.begin(), NULL))
|
||||||
@ -169,7 +214,6 @@ std::error_code create_link(const Twine &to, const Twine &from) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::error_code remove(const Twine &path, bool IgnoreNonExisting) {
|
std::error_code remove(const Twine &path, bool IgnoreNonExisting) {
|
||||||
SmallString<128> path_storage;
|
|
||||||
SmallVector<wchar_t, 128> path_utf16;
|
SmallVector<wchar_t, 128> path_utf16;
|
||||||
|
|
||||||
file_status ST;
|
file_status ST;
|
||||||
@ -179,8 +223,7 @@ std::error_code remove(const Twine &path, bool IgnoreNonExisting) {
|
|||||||
return std::error_code();
|
return std::error_code();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::error_code ec =
|
if (std::error_code ec = widenPath(path, path_utf16))
|
||||||
UTF8ToUTF16(path.toStringRef(path_storage), path_utf16))
|
|
||||||
return ec;
|
return ec;
|
||||||
|
|
||||||
if (ST.type() == file_type::directory_file) {
|
if (ST.type() == file_type::directory_file) {
|
||||||
@ -200,18 +243,12 @@ std::error_code remove(const Twine &path, bool IgnoreNonExisting) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::error_code rename(const Twine &from, const Twine &to) {
|
std::error_code rename(const Twine &from, const Twine &to) {
|
||||||
// Get arguments.
|
|
||||||
SmallString<128> from_storage;
|
|
||||||
SmallString<128> to_storage;
|
|
||||||
StringRef f = from.toStringRef(from_storage);
|
|
||||||
StringRef t = to.toStringRef(to_storage);
|
|
||||||
|
|
||||||
// Convert to utf-16.
|
// Convert to utf-16.
|
||||||
SmallVector<wchar_t, 128> wide_from;
|
SmallVector<wchar_t, 128> wide_from;
|
||||||
SmallVector<wchar_t, 128> wide_to;
|
SmallVector<wchar_t, 128> wide_to;
|
||||||
if (std::error_code ec = UTF8ToUTF16(f, wide_from))
|
if (std::error_code ec = widenPath(from, wide_from))
|
||||||
return ec;
|
return ec;
|
||||||
if (std::error_code ec = UTF8ToUTF16(t, wide_to))
|
if (std::error_code ec = widenPath(to, wide_to))
|
||||||
return ec;
|
return ec;
|
||||||
|
|
||||||
std::error_code ec = std::error_code();
|
std::error_code ec = std::error_code();
|
||||||
@ -232,11 +269,9 @@ std::error_code rename(const Twine &from, const Twine &to) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::error_code resize_file(const Twine &path, uint64_t size) {
|
std::error_code resize_file(const Twine &path, uint64_t size) {
|
||||||
SmallString<128> path_storage;
|
|
||||||
SmallVector<wchar_t, 128> path_utf16;
|
SmallVector<wchar_t, 128> path_utf16;
|
||||||
|
|
||||||
if (std::error_code ec =
|
if (std::error_code ec = widenPath(path, path_utf16))
|
||||||
UTF8ToUTF16(path.toStringRef(path_storage), path_utf16))
|
|
||||||
return ec;
|
return ec;
|
||||||
|
|
||||||
int fd = ::_wopen(path_utf16.begin(), O_BINARY | _O_RDWR, S_IWRITE);
|
int fd = ::_wopen(path_utf16.begin(), O_BINARY | _O_RDWR, S_IWRITE);
|
||||||
@ -252,11 +287,9 @@ std::error_code resize_file(const Twine &path, uint64_t size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::error_code access(const Twine &Path, AccessMode Mode) {
|
std::error_code access(const Twine &Path, AccessMode Mode) {
|
||||||
SmallString<128> PathStorage;
|
|
||||||
SmallVector<wchar_t, 128> PathUtf16;
|
SmallVector<wchar_t, 128> PathUtf16;
|
||||||
|
|
||||||
if (std::error_code EC =
|
if (std::error_code EC = widenPath(Path, PathUtf16))
|
||||||
UTF8ToUTF16(Path.toStringRef(PathStorage), PathUtf16))
|
|
||||||
return EC;
|
return EC;
|
||||||
|
|
||||||
DWORD Attributes = ::GetFileAttributesW(PathUtf16.begin());
|
DWORD Attributes = ::GetFileAttributesW(PathUtf16.begin());
|
||||||
@ -382,7 +415,7 @@ std::error_code status(const Twine &path, file_status &result) {
|
|||||||
return std::error_code();
|
return std::error_code();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::error_code ec = UTF8ToUTF16(path8, path_utf16))
|
if (std::error_code ec = widenPath(path8, path_utf16))
|
||||||
return ec;
|
return ec;
|
||||||
|
|
||||||
DWORD attr = ::GetFileAttributesW(path_utf16.begin());
|
DWORD attr = ::GetFileAttributesW(path_utf16.begin());
|
||||||
@ -525,11 +558,10 @@ mapped_file_region::mapped_file_region(const Twine &path,
|
|||||||
, FileDescriptor()
|
, FileDescriptor()
|
||||||
, FileHandle(INVALID_HANDLE_VALUE)
|
, FileHandle(INVALID_HANDLE_VALUE)
|
||||||
, FileMappingHandle() {
|
, FileMappingHandle() {
|
||||||
SmallString<128> path_storage;
|
|
||||||
SmallVector<wchar_t, 128> path_utf16;
|
SmallVector<wchar_t, 128> path_utf16;
|
||||||
|
|
||||||
// Convert path to UTF-16.
|
// Convert path to UTF-16.
|
||||||
if ((ec = UTF8ToUTF16(path.toStringRef(path_storage), path_utf16)))
|
if (ec = widenPath(path, path_utf16))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Get file handle for creating a file mapping.
|
// Get file handle for creating a file mapping.
|
||||||
@ -635,7 +667,7 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
|
|||||||
StringRef path){
|
StringRef path){
|
||||||
SmallVector<wchar_t, 128> path_utf16;
|
SmallVector<wchar_t, 128> path_utf16;
|
||||||
|
|
||||||
if (std::error_code ec = UTF8ToUTF16(path, path_utf16))
|
if (std::error_code ec = widenPath(path, path_utf16))
|
||||||
return ec;
|
return ec;
|
||||||
|
|
||||||
// Convert path to the format that Windows is happy with.
|
// Convert path to the format that Windows is happy with.
|
||||||
@ -718,11 +750,9 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &it) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::error_code openFileForRead(const Twine &Name, int &ResultFD) {
|
std::error_code openFileForRead(const Twine &Name, int &ResultFD) {
|
||||||
SmallString<128> PathStorage;
|
|
||||||
SmallVector<wchar_t, 128> PathUTF16;
|
SmallVector<wchar_t, 128> PathUTF16;
|
||||||
|
|
||||||
if (std::error_code EC =
|
if (std::error_code EC = widenPath(Name, PathUTF16))
|
||||||
UTF8ToUTF16(Name.toStringRef(PathStorage), PathUTF16))
|
|
||||||
return EC;
|
return EC;
|
||||||
|
|
||||||
HANDLE H = ::CreateFileW(PathUTF16.begin(), GENERIC_READ,
|
HANDLE H = ::CreateFileW(PathUTF16.begin(), GENERIC_READ,
|
||||||
@ -757,11 +787,9 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
|
|||||||
assert((!(Flags & sys::fs::F_Excl) || !(Flags & sys::fs::F_Append)) &&
|
assert((!(Flags & sys::fs::F_Excl) || !(Flags & sys::fs::F_Append)) &&
|
||||||
"Cannot specify both 'excl' and 'append' file creation flags!");
|
"Cannot specify both 'excl' and 'append' file creation flags!");
|
||||||
|
|
||||||
SmallString<128> PathStorage;
|
|
||||||
SmallVector<wchar_t, 128> PathUTF16;
|
SmallVector<wchar_t, 128> PathUTF16;
|
||||||
|
|
||||||
if (std::error_code EC =
|
if (std::error_code EC = widenPath(Name, PathUTF16))
|
||||||
UTF8ToUTF16(Name.toStringRef(PathStorage), PathUTF16))
|
|
||||||
return EC;
|
return EC;
|
||||||
|
|
||||||
DWORD CreationDisposition;
|
DWORD CreationDisposition;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
#ifdef LLVM_ON_WIN32
|
#ifdef LLVM_ON_WIN32
|
||||||
|
#include <Windows.h>
|
||||||
#include <winerror.h>
|
#include <winerror.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -261,7 +262,7 @@ TEST(Support, HomeDirectory) {
|
|||||||
class FileSystemTest : public testing::Test {
|
class FileSystemTest : public testing::Test {
|
||||||
protected:
|
protected:
|
||||||
/// Unique temporary directory in which all created filesystem entities must
|
/// Unique temporary directory in which all created filesystem entities must
|
||||||
/// be placed. It is recursively removed at the end of each test.
|
/// be placed. It is removed at the end of each test (must be empty).
|
||||||
SmallString<128> TestDirectory;
|
SmallString<128> TestDirectory;
|
||||||
|
|
||||||
virtual void SetUp() {
|
virtual void SetUp() {
|
||||||
@ -397,7 +398,15 @@ TEST_F(FileSystemTest, TempFiles) {
|
|||||||
"abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz2"
|
"abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz2"
|
||||||
"abcdefghijklmnopqrstuvwxyz1abcdefghijklmnopqrstuvwxyz0";
|
"abcdefghijklmnopqrstuvwxyz1abcdefghijklmnopqrstuvwxyz0";
|
||||||
EXPECT_EQ(fs::createUniqueFile(Twine(Path270), FileDescriptor, TempPath),
|
EXPECT_EQ(fs::createUniqueFile(Twine(Path270), FileDescriptor, TempPath),
|
||||||
errc::no_such_file_or_directory);
|
errc::invalid_argument);
|
||||||
|
// Relative path < 247 chars, no problem.
|
||||||
|
const char *Path216 =
|
||||||
|
"abcdefghijklmnopqrstuvwxyz7abcdefghijklmnopqrstuvwxyz6"
|
||||||
|
"abcdefghijklmnopqrstuvwxyz5abcdefghijklmnopqrstuvwxyz4"
|
||||||
|
"abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz2"
|
||||||
|
"abcdefghijklmnopqrstuvwxyz1abcdefghijklmnopqrstuvwxyz0";
|
||||||
|
ASSERT_NO_ERROR(fs::createTemporaryFile(Twine(Path216), "", TempPath));
|
||||||
|
ASSERT_NO_ERROR(fs::remove(Twine(TempPath)));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,6 +416,54 @@ TEST_F(FileSystemTest, CreateDir) {
|
|||||||
ASSERT_EQ(fs::create_directory(Twine(TestDirectory) + "foo", false),
|
ASSERT_EQ(fs::create_directory(Twine(TestDirectory) + "foo", false),
|
||||||
errc::file_exists);
|
errc::file_exists);
|
||||||
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "foo"));
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "foo"));
|
||||||
|
|
||||||
|
#ifdef LLVM_ON_WIN32
|
||||||
|
// Prove that create_directories() can handle a pathname > 248 characters,
|
||||||
|
// which is the documented limit for CreateDirectory().
|
||||||
|
// (248 is MAX_PATH subtracting room for an 8.3 filename.)
|
||||||
|
// Generate a directory path guaranteed to fall into that range.
|
||||||
|
size_t TmpLen = TestDirectory.size();
|
||||||
|
const char *OneDir = "\\123456789";
|
||||||
|
size_t OneDirLen = strlen(OneDir);
|
||||||
|
ASSERT_LT(OneDirLen, 12);
|
||||||
|
size_t NLevels = ((248 - TmpLen) / OneDirLen) + 1;
|
||||||
|
SmallString<260> LongDir(TestDirectory);
|
||||||
|
for (size_t I = 0; I < NLevels; ++I)
|
||||||
|
LongDir.append(OneDir);
|
||||||
|
ASSERT_NO_ERROR(fs::create_directories(Twine(LongDir)));
|
||||||
|
ASSERT_NO_ERROR(fs::create_directories(Twine(LongDir)));
|
||||||
|
ASSERT_EQ(fs::create_directories(Twine(LongDir), false),
|
||||||
|
errc::file_exists);
|
||||||
|
// Tidy up, "recursively" removing the directories.
|
||||||
|
StringRef ThisDir(LongDir);
|
||||||
|
for (size_t J = 0; J < NLevels; ++J) {
|
||||||
|
ASSERT_NO_ERROR(fs::remove(ThisDir));
|
||||||
|
ThisDir = path::parent_path(ThisDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similarly for a relative pathname. Need to set the current directory to
|
||||||
|
// TestDirectory so that the one we create ends up in the right place.
|
||||||
|
char PreviousDir[260];
|
||||||
|
size_t PreviousDirLen = ::GetCurrentDirectoryA(260, PreviousDir);
|
||||||
|
ASSERT_GT(PreviousDirLen, 0);
|
||||||
|
ASSERT_LT(PreviousDirLen, 260);
|
||||||
|
ASSERT_NE(::SetCurrentDirectoryA(TestDirectory.c_str()), 0);
|
||||||
|
LongDir.clear();
|
||||||
|
// Generate a relative directory name with absolute length > 248.
|
||||||
|
size_t LongDirLen = 249 - TestDirectory.size();
|
||||||
|
LongDir.assign(LongDirLen, 'a');
|
||||||
|
ASSERT_NO_ERROR(fs::create_directory(Twine(LongDir)));
|
||||||
|
// While we're here, prove that .. and . handling works in these long paths.
|
||||||
|
const char *DotDotDirs = "\\..\\.\\b";
|
||||||
|
LongDir.append(DotDotDirs);
|
||||||
|
ASSERT_NO_ERROR(fs::create_directory(Twine("b")));
|
||||||
|
ASSERT_EQ(fs::create_directory(Twine(LongDir), false), errc::file_exists);
|
||||||
|
// And clean up.
|
||||||
|
ASSERT_NO_ERROR(fs::remove(Twine("b")));
|
||||||
|
ASSERT_NO_ERROR(fs::remove(
|
||||||
|
Twine(LongDir.substr(0, LongDir.size() - strlen(DotDotDirs)))));
|
||||||
|
ASSERT_NE(::SetCurrentDirectoryA(PreviousDir), 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FileSystemTest, DirectoryIteration) {
|
TEST_F(FileSystemTest, DirectoryIteration) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user