From f8fbefd951af2a21f5f85bb6d711b9d0bf636af2 Mon Sep 17 00:00:00 2001 From: Zichang Guo Date: Tue, 8 Sep 2020 22:34:50 +0000 Subject: [PATCH] Revert "Enable long path on Windows" This reverts commit 08662f05758acbf4a80b843b06c51a6a430bfb3e. Reason for revert: failure on pkg-win-release, https://dart-ci.appspot.com/log/pkg-win-release/unittest-asserts-release-win/12878/pkg/dartdev/test/commands/test_test Original change's description: > Enable long path on Windows > > File APIs on Windows can now handle files and directories identified by > long paths (greater than 260 characters). For directory, the limit is > 248. > > Some restrictions from Windows: > 1. The size limit for long path is 32,767 characters. > 2. Each component separated by backslashes should not be more than 255 > characters. > > Reference: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation. > > Note that `Directory.current` does not work with long path. > > Bug: https://github.com/dart-lang/sdk/issues/42416 > Change-Id: Ia1b4608d393fb36f1d843858c6f076f3c825dc83 > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/152736 > Reviewed-by: Alexander Aprelev > Commit-Queue: Zichang Guo TBR=aam@google.com,zra@google.com,zichangguo@google.com Change-Id: If6701c887e616cd62f20faac43f601cfb53ed349 No-Presubmit: true No-Tree-Checks: true No-Try: true Bug: https://github.com/dart-lang/sdk/issues/42416 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/162104 Reviewed-by: Zichang Guo Commit-Queue: Zichang Guo --- CHANGELOG.md | 5 - runtime/bin/builtin_impl_sources.gni | 1 - runtime/bin/directory_win.cc | 51 ++-- runtime/bin/file_win.cc | 182 ++++--------- runtime/bin/file_win.h | 24 -- runtime/bin/namespace_win.cc | 5 - runtime/bin/utils_win.h | 2 - tests/standalone/io/file_long_path_test.dart | 247 ------------------ .../io/file_relative_long_path_test.dart | 39 --- .../standalone_2/io/file_long_path_test.dart | 247 ------------------ .../io/file_relative_long_path_test.dart | 39 --- 11 files changed, 65 insertions(+), 777 deletions(-) delete mode 100644 runtime/bin/file_win.h delete mode 100644 tests/standalone/io/file_long_path_test.dart delete mode 100644 tests/standalone/io/file_relative_long_path_test.dart delete mode 100644 tests/standalone_2/io/file_long_path_test.dart delete mode 100644 tests/standalone_2/io/file_relative_long_path_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 15b48ba6334..9b56ce544fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,14 +130,9 @@ applications (issue [flutter/flutter#63038][]). * [Abstract Unix Domain Socket][] is supported on Linux/Android now. Using an `InternetAddress` with `address` starting with '@' and type being `InternetAddressType.Unix` will create an abstract Unix Domain Socket. -* On Windows, file APIs can now handle files and directories identified by - long paths (greater than 260 characters). It complies with all restrictions - from [Long Path on Windows][]. Note that `Directory.current` does not work - with long path. [#42006]: https://github.com/dart-lang/sdk/issues/42006 [Abstract Unix Domain Socket]: http://man7.org/linux/man-pages/man7/unix.7.html -[Long Path on Windows]: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation #### `dart:html` diff --git a/runtime/bin/builtin_impl_sources.gni b/runtime/bin/builtin_impl_sources.gni index 58323d0f0cd..b8ea4eb808e 100644 --- a/runtime/bin/builtin_impl_sources.gni +++ b/runtime/bin/builtin_impl_sources.gni @@ -45,7 +45,6 @@ builtin_impl_sources = [ "file_macos.cc", "file_support.cc", "file_win.cc", - "file_win.h", "io_buffer.cc", "io_buffer.h", "isolate_data.cc", diff --git a/runtime/bin/directory_win.cc b/runtime/bin/directory_win.cc index 16b09846da3..ec968633191 100644 --- a/runtime/bin/directory_win.cc +++ b/runtime/bin/directory_win.cc @@ -13,7 +13,6 @@ #include "bin/crypto.h" #include "bin/dartutils.h" #include "bin/file.h" -#include "bin/file_win.h" #include "bin/namespace.h" #include "bin/utils.h" #include "bin/utils_win.h" @@ -22,6 +21,8 @@ #undef DeleteFile +#define MAX_LONG_PATH 32767 + namespace dart { namespace bin { @@ -277,12 +278,7 @@ static bool DeleteEntry(LPWIN32_FIND_DATAW find_file_data, PathBuffer* path) { } static bool DeleteRecursively(PathBuffer* path) { - PathBuffer prefixed_path; - if (!prefixed_path.Add(PrefixLongDirectoryPath(path->AsScopedString()))) { - return false; - } - - DWORD attributes = GetFileAttributesW(prefixed_path.AsStringW()); + DWORD attributes = GetFileAttributesW(path->AsStringW()); if (attributes == INVALID_FILE_ATTRIBUTES) { return false; } @@ -290,34 +286,33 @@ static bool DeleteRecursively(PathBuffer* path) { // filesystem that we do not want to recurse into. if ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { // Just delete the junction itself. - return RemoveDirectoryW(prefixed_path.AsStringW()) != 0; + return RemoveDirectoryW(path->AsStringW()) != 0; } // If it's a file, remove it directly. if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { - return DeleteFile(L"", &prefixed_path); + return DeleteFile(L"", path); } - if (!prefixed_path.AddW(L"\\*")) { + if (!path->AddW(L"\\*")) { return false; } WIN32_FIND_DATAW find_file_data; - HANDLE find_handle = - FindFirstFileW(prefixed_path.AsStringW(), &find_file_data); + HANDLE find_handle = FindFirstFileW(path->AsStringW(), &find_file_data); if (find_handle == INVALID_HANDLE_VALUE) { return false; } // Adjust the path by removing the '*' used for the search. - int path_length = prefixed_path.length() - 1; - prefixed_path.Reset(path_length); + int path_length = path->length() - 1; + path->Reset(path_length); do { - if (!DeleteEntry(&find_file_data, &prefixed_path)) { + if (!DeleteEntry(&find_file_data, path)) { break; } - prefixed_path.Reset(path_length); // DeleteEntry adds to the path. + path->Reset(path_length); // DeleteEntry adds to the path. } while (FindNextFileW(find_handle, &find_file_data) != 0); DWORD last_error = GetLastError(); @@ -329,9 +324,8 @@ static bool DeleteRecursively(PathBuffer* path) { return false; } // All content deleted succesfully, try to delete directory. - prefixed_path.Reset(path_length - - 1); // Drop the "\" from the end of the path. - return RemoveDirectoryW(prefixed_path.AsStringW()) != 0; + path->Reset(path_length - 1); // Drop the "\" from the end of the path. + return RemoveDirectoryW(path->AsStringW()) != 0; } static Directory::ExistsResult ExistsHelper(const wchar_t* dir_name) { @@ -355,8 +349,7 @@ static Directory::ExistsResult ExistsHelper(const wchar_t* dir_name) { Directory::ExistsResult Directory::Exists(Namespace* namespc, const char* dir_name) { - const char* prefixed_dir_name = PrefixLongDirectoryPath(dir_name); - Utf8ToWideScope system_name(prefixed_dir_name); + Utf8ToWideScope system_name(dir_name); return ExistsHelper(system_name.wide()); } @@ -376,8 +369,7 @@ char* Directory::CurrentNoScope() { } bool Directory::Create(Namespace* namespc, const char* dir_name) { - const char* prefixed_dir_name = PrefixLongDirectoryPath(dir_name); - Utf8ToWideScope system_name(prefixed_dir_name); + Utf8ToWideScope system_name(dir_name); int create_status = CreateDirectoryW(system_name.wide(), NULL); // If the directory already existed, treat it as a success. if ((create_status == 0) && (GetLastError() == ERROR_ALREADY_EXISTS) && @@ -483,11 +475,10 @@ const char* Directory::CreateTemp(Namespace* namespc, const char* prefix) { bool Directory::Delete(Namespace* namespc, const char* dir_name, bool recursive) { - const char* prefixed_dir_name = PrefixLongDirectoryPath(dir_name); bool result = false; - Utf8ToWideScope system_dir_name(prefixed_dir_name); + Utf8ToWideScope system_dir_name(dir_name); if (!recursive) { - if (File::GetType(namespc, prefixed_dir_name, true) == File::kIsDirectory) { + if (File::GetType(namespc, dir_name, true) == File::kIsDirectory) { result = (RemoveDirectoryW(system_dir_name.wide()) != 0); } else { SetLastError(ERROR_FILE_NOT_FOUND); @@ -504,20 +495,18 @@ bool Directory::Delete(Namespace* namespc, bool Directory::Rename(Namespace* namespc, const char* path, const char* new_path) { - const char* prefixed_dir = PrefixLongDirectoryPath(path); - Utf8ToWideScope system_path(prefixed_dir); + Utf8ToWideScope system_path(path); + Utf8ToWideScope system_new_path(new_path); ExistsResult exists = ExistsHelper(system_path.wide()); if (exists != EXISTS) { return false; } - const char* prefixed_new_dir = PrefixLongDirectoryPath(new_path); - Utf8ToWideScope system_new_path(prefixed_new_dir); ExistsResult new_exists = ExistsHelper(system_new_path.wide()); // MoveFile does not allow replacing existing directories. Therefore, // if the new_path is currently a directory we need to delete it // first. if (new_exists == EXISTS) { - bool success = Delete(namespc, prefixed_new_dir, true); + bool success = Delete(namespc, new_path, true); if (!success) { return false; } diff --git a/runtime/bin/file_win.cc b/runtime/bin/file_win.cc index ee282840161..21f8df23f4e 100644 --- a/runtime/bin/file_win.cc +++ b/runtime/bin/file_win.cc @@ -5,11 +5,13 @@ #include "platform/globals.h" #if defined(HOST_OS_WINDOWS) -#include // NOLINT +#include "bin/file.h" + #include // NOLINT #include // NOLINT #include // NOLINT -#undef StrDup // defined in Shlwapi.h as StrDupW +#include // NOLINT +#undef StrDup // defined in Shlwapi.h as StrDupW #include // NOLINT #include // NOLINT #include // NOLINT @@ -18,8 +20,6 @@ #include "bin/builtin.h" #include "bin/crypto.h" #include "bin/directory.h" -#include "bin/file.h" -#include "bin/file_win.h" #include "bin/namespace.h" #include "bin/utils.h" #include "bin/utils_win.h" @@ -299,7 +299,7 @@ File* File::FileOpenW(const wchar_t* system_name, FileOpenMode mode) { } File* File::Open(Namespace* namespc, const char* path, FileOpenMode mode) { - Utf8ToWideScope system_name(PrefixLongFilePath(path)); + Utf8ToWideScope system_name(path); File* file = FileOpenW(system_name.wide(), mode); return file; } @@ -365,7 +365,7 @@ static bool StatHelper(wchar_t* path, struct __stat64* st) { bool File::Exists(Namespace* namespc, const char* name) { struct __stat64 st; - Utf8ToWideScope system_name(PrefixLongFilePath(name)); + Utf8ToWideScope system_name(name); return StatHelper(system_name.wide(), &st); } @@ -379,7 +379,7 @@ bool File::ExistsUri(Namespace* namespc, const char* uri) { } bool File::Create(Namespace* namespc, const char* name) { - Utf8ToWideScope system_name(PrefixLongFilePath(name)); + Utf8ToWideScope system_name(name); int fd = _wopen(system_name.wide(), O_RDONLY | O_CREAT, 0666); if (fd < 0) { return false; @@ -426,8 +426,8 @@ static const int kMountPointHeaderSize = 4 * sizeof(USHORT); bool File::CreateLink(Namespace* namespc, const char* utf8_name, const char* utf8_target) { - Utf8ToWideScope name(PrefixLongFilePath(utf8_name)); - Utf8ToWideScope target(PrefixLongFilePath(utf8_target)); + Utf8ToWideScope name(utf8_name); + Utf8ToWideScope target(utf8_target); DWORD flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; File::Type type = File::GetType(namespc, utf8_target, true); @@ -449,13 +449,13 @@ bool File::CreateLink(Namespace* namespc, } bool File::Delete(Namespace* namespc, const char* name) { - Utf8ToWideScope system_name(PrefixLongFilePath(name)); + Utf8ToWideScope system_name(name); int status = _wremove(system_name.wide()); return status != -1; } bool File::DeleteLink(Namespace* namespc, const char* name) { - Utf8ToWideScope system_name(PrefixLongFilePath(name)); + Utf8ToWideScope system_name(name); bool result = false; DWORD attributes = GetFileAttributesW(system_name.wide()); if ((attributes == INVALID_FILE_ATTRIBUTES) || @@ -477,15 +477,13 @@ bool File::DeleteLink(Namespace* namespc, const char* name) { bool File::Rename(Namespace* namespc, const char* old_path, const char* new_path) { - const char* prefixed_old_path = PrefixLongFilePath(old_path); - File::Type type = GetType(namespc, prefixed_old_path, false); + File::Type type = GetType(namespc, old_path, false); if (type != kIsFile) { SetLastError(ERROR_FILE_NOT_FOUND); return false; } - const char* prefixed_new_path = PrefixLongFilePath(new_path); - Utf8ToWideScope system_old_path(prefixed_old_path); - Utf8ToWideScope system_new_path(prefixed_new_path); + Utf8ToWideScope system_old_path(old_path); + Utf8ToWideScope system_new_path(new_path); DWORD flags = MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING; int move_status = MoveFileExW(system_old_path.wide(), system_new_path.wide(), flags); @@ -495,25 +493,23 @@ bool File::Rename(Namespace* namespc, bool File::RenameLink(Namespace* namespc, const char* old_path, const char* new_path) { - const char* prefixed_old_path = PrefixLongFilePath(old_path); - File::Type type = GetType(namespc, prefixed_old_path, false); + File::Type type = GetType(namespc, old_path, false); if (type != kIsLink) { SetLastError(ERROR_FILE_NOT_FOUND); return false; } - Utf8ToWideScope system_old_path(prefixed_old_path); - const char* prefixed_new_path = PrefixLongFilePath(new_path); - Utf8ToWideScope system_new_path(prefixed_new_path); + Utf8ToWideScope system_old_path(old_path); + Utf8ToWideScope system_new_path(new_path); DWORD flags = MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING; // Junction links on Windows appear as special directories. MoveFileExW's // MOVEFILE_REPLACE_EXISTING does not allow for replacement of directories, // so we need to remove it before renaming a link. This step is only // necessary for junctions created by the old Link.create implementation. - if ((Directory::Exists(namespc, prefixed_new_path) == Directory::EXISTS) && - (GetType(namespc, prefixed_new_path, false) == kIsLink)) { + if ((Directory::Exists(namespc, new_path) == Directory::EXISTS) && + (GetType(namespc, new_path, false) == kIsLink)) { // Bail out if the DeleteLink call fails. - if (!DeleteLink(namespc, prefixed_new_path)) { + if (!DeleteLink(namespc, new_path)) { return false; } } @@ -619,26 +615,24 @@ static wchar_t* CopyIntoTempFile(const char* src, const char* dest) { bool File::Copy(Namespace* namespc, const char* old_path, const char* new_path) { - const char* prefixed_old_path = PrefixLongFilePath(old_path); - const char* prefixed_new_path = PrefixLongFilePath(new_path); - File::Type type = GetType(namespc, prefixed_old_path, false); + File::Type type = GetType(namespc, old_path, false); if (type != kIsFile) { SetLastError(ERROR_FILE_NOT_FOUND); return false; } - wchar_t* temp_file = CopyIntoTempFile(prefixed_old_path, prefixed_new_path); + wchar_t* temp_file = CopyIntoTempFile(old_path, new_path); if (temp_file == NULL) { // If temp file creation fails, fall back on doing a direct copy. - Utf8ToWideScope system_old_path(prefixed_old_path); - Utf8ToWideScope system_new_path(prefixed_new_path); + Utf8ToWideScope system_old_path(old_path); + Utf8ToWideScope system_new_path(new_path); return CopyFileExW(system_old_path.wide(), system_new_path.wide(), NULL, NULL, NULL, 0) != 0; } - Utf8ToWideScope system_new_dest(prefixed_new_path); + Utf8ToWideScope system_new_dest(new_path); // Remove the existing file. Otherwise, renaming will fail. - if (Exists(namespc, prefixed_new_path)) { + if (Exists(namespc, new_path)) { DeleteFileW(system_new_dest.wide()); } @@ -653,7 +647,7 @@ bool File::Copy(Namespace* namespc, int64_t File::LengthFromPath(Namespace* namespc, const char* name) { struct __stat64 st; - Utf8ToWideScope system_name(PrefixLongFilePath(name)); + Utf8ToWideScope system_name(name); if (!StatHelper(system_name.wide(), &st)) { return -1; } @@ -664,8 +658,7 @@ const char* File::LinkTarget(Namespace* namespc, const char* pathname, char* dest, int dest_size) { - const wchar_t* name = - StringUtilsWin::Utf8ToWide(PrefixLongFilePath(pathname)); + const wchar_t* name = StringUtilsWin::Utf8ToWide(pathname); HANDLE dir_handle = CreateFileW( name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, @@ -675,10 +668,8 @@ const char* File::LinkTarget(Namespace* namespc, return NULL; } - // Allocate a buffer for regular paths (smaller than MAX_PATH). If buffer is - // too small for a long path, allocate a bigger buffer and try again. int buffer_size = - sizeof(REPARSE_DATA_BUFFER) + (MAX_PATH + 1) * sizeof(WCHAR); + sizeof(REPARSE_DATA_BUFFER) + 2 * (MAX_PATH + 1) * sizeof(WCHAR); REPARSE_DATA_BUFFER* buffer = reinterpret_cast(Dart_ScopeAllocate(buffer_size)); DWORD received_bytes; // Value is not used. @@ -686,26 +677,9 @@ const char* File::LinkTarget(Namespace* namespc, buffer, buffer_size, &received_bytes, NULL); if (result == 0) { DWORD error = GetLastError(); - // If ERROR_MORE_DATA is thrown, the target path exceeds the size limit. A - // bigger buffer will be required. - if (error == ERROR_MORE_DATA) { - // Allocate a bigger buffer with MAX_LONG_PATH - buffer_size = - sizeof(REPARSE_DATA_BUFFER) + (MAX_LONG_PATH + 1) * sizeof(WCHAR); - buffer = reinterpret_cast( - Dart_ScopeAllocate(buffer_size)); - result = DeviceIoControl(dir_handle, FSCTL_GET_REPARSE_POINT, NULL, 0, - buffer, buffer_size, &received_bytes, NULL); - if (result == 0) { - // Overwrite the ERROR_MORE_DATA. - error = GetLastError(); - } - } - if (result == 0) { - CloseHandle(dir_handle); - SetLastError(error); - return NULL; - } + CloseHandle(dir_handle); + SetLastError(error); + return NULL; } if (CloseHandle(dir_handle) == 0) { return NULL; @@ -752,12 +726,11 @@ const char* File::LinkTarget(Namespace* namespc, } void File::Stat(Namespace* namespc, const char* name, int64_t* data) { - const char* prefixed_name = PrefixLongFilePath(name); - File::Type type = GetType(namespc, prefixed_name, false); + File::Type type = GetType(namespc, name, false); data[kType] = type; if (type != kDoesNotExist) { struct _stat64 st; - Utf8ToWideScope system_name(prefixed_name); + Utf8ToWideScope system_name(name); int stat_status = _wstat64(system_name.wide(), &st); if (stat_status == 0) { data[kCreatedTime] = st.st_ctime * 1000; @@ -773,7 +746,7 @@ void File::Stat(Namespace* namespc, const char* name, int64_t* data) { time_t File::LastAccessed(Namespace* namespc, const char* name) { struct __stat64 st; - Utf8ToWideScope system_name(PrefixLongFilePath(name)); + Utf8ToWideScope system_name(name); if (!StatHelper(system_name.wide(), &st)) { return -1; } @@ -782,7 +755,7 @@ time_t File::LastAccessed(Namespace* namespc, const char* name) { time_t File::LastModified(Namespace* namespc, const char* name) { struct __stat64 st; - Utf8ToWideScope system_name(PrefixLongFilePath(name)); + Utf8ToWideScope system_name(name); if (!StatHelper(system_name.wide(), &st)) { return -1; } @@ -794,7 +767,7 @@ bool File::SetLastAccessed(Namespace* namespc, int64_t millis) { // First get the current times. struct __stat64 st; - Utf8ToWideScope system_name(PrefixLongFilePath(name)); + Utf8ToWideScope system_name(name); if (!StatHelper(system_name.wide(), &st)) { return false; } @@ -811,7 +784,7 @@ bool File::SetLastModified(Namespace* namespc, int64_t millis) { // First get the current times. struct __stat64 st; - Utf8ToWideScope system_name(PrefixLongFilePath(name)); + Utf8ToWideScope system_name(name); if (!StatHelper(system_name.wide(), &st)) { return false; } @@ -828,6 +801,7 @@ bool File::SetLastModified(Namespace* namespc, bool File::IsAbsolutePath(const char* pathname) { if (pathname == NULL) return false; char first = pathname[0]; + if (pathname == 0) return false; char second = pathname[1]; if (first == '\\' && second == '\\') return true; if (second != ':') return false; @@ -836,77 +810,11 @@ bool File::IsAbsolutePath(const char* pathname) { return (first >= 'a') && (first <= 'z') && (third == '\\' || third == '/'); } -const char* PrefixLongFilePath(const char* path) { - return PrefixLongPathIfExceedLimit(path, true); -} - -const char* PrefixLongDirectoryPath(const char* path) { - return PrefixLongPathIfExceedLimit(path, false); -} - -const char* PrefixLongPathIfExceedLimit(const char* path, bool file_name) { - const char* prefix = "\\\\?\\"; - // File name and Directory name has a different size limit. - // Reference: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation - int limit = file_name ? MAX_PATH : MAX_DIRECTORY_SIZE; - int length; - if (!File::IsAbsolutePath(path)) { - // Convert relative paths to absolute paths because relative paths don't - // work when they represent long(>MAX_PATH) absolute paths. - wchar_t* buffer = - reinterpret_cast(malloc(sizeof(wchar_t) * (limit + 1))); - Utf8ToWideScope dir(path); - LPWSTR* file = NULL; - int size = GetFullPathNameW(dir.wide(), limit + 1, buffer, file); - free(buffer); - if (size == 0) { - return path; - } - if (size > limit) { - // The absolute path is a long path. Assign a buffer with right size and - // try again. - // Note that in this case, size represents the size of the path plus the - // null terminator. - wchar_t* buf = - reinterpret_cast(malloc(sizeof(wchar_t) * (size))); - size = GetFullPathNameW(dir.wide(), size, buf, file); - if (size == 0) { - return path; - } - path = StringUtilsWin::WideToUtf8(buf); - free(buf); - } - // length doesn't include null terminator on success return. - length = size; - } else { - length = strlen(path); - } - if (length <= limit) { - return path; - } - int length_prefix = strlen(prefix); - // If a path is already prefixed, return it. - if ((length > length_prefix && strncmp(path, prefix, length_prefix) == 0)) { - return path; - } - // Replace forward slashes with backward slashes. - char* result = reinterpret_cast( - Dart_ScopeAllocate((length_prefix + 1 + length) * sizeof(char))); - - strncpy(result, prefix, length_prefix); - - for (int i = 0; i < length; i++) { - result[length_prefix + i] = path[i] == '/' ? '\\' : path[i]; - } - result[length + length_prefix] = '\0'; - return result; -} - const char* File::GetCanonicalPath(Namespace* namespc, const char* pathname, char* dest, int dest_size) { - Utf8ToWideScope system_name(PrefixLongFilePath(pathname)); + Utf8ToWideScope system_name(pathname); HANDLE file_handle = CreateFileW(system_name.wide(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); @@ -930,8 +838,9 @@ const char* File::GetCanonicalPath(Namespace* namespc, // Remove leading \\?\ if possible, unless input used it. int offset = 0; - if ((result_size > 4) && (wcsncmp(path.get(), L"\\\\?\\", 4) == 0) && - (strncmp(pathname, "\\\\?\\", 4) != 0)) { + if ((result_size < MAX_PATH - 1 + 4) && (result_size > 4) && + (wcsncmp(path.get(), L"\\\\?\\", 4) == 0) && + (wcsncmp(system_name.wide(), L"\\\\?\\", 4) != 0)) { offset = 4; } int utf8_size = WideCharToMultiByte(CP_UTF8, 0, path.get() + offset, -1, @@ -970,7 +879,7 @@ File::Type File::GetType(Namespace* namespc, const char* pathname, bool follow_links) { // Convert to wchar_t string. - Utf8ToWideScope name(PrefixLongFilePath(pathname)); + Utf8ToWideScope name(pathname); DWORD attributes = GetFileAttributesW(name.wide()); File::Type result = kIsFile; if (attributes == INVALID_FILE_ATTRIBUTES) { @@ -1010,8 +919,7 @@ File::Identical File::AreIdentical(Namespace* namespc_1, USE(namespc_1); USE(namespc_2); BY_HANDLE_FILE_INFORMATION file_info[2]; - const char* file_names[2] = {PrefixLongFilePath(file_1), - PrefixLongFilePath(file_2)}; + const char* file_names[2] = {file_1, file_2}; for (int i = 0; i < 2; ++i) { Utf8ToWideScope wide_name(file_names[i]); HANDLE file_handle = CreateFileW( diff --git a/runtime/bin/file_win.h b/runtime/bin/file_win.h deleted file mode 100644 index 8993438457d..00000000000 --- a/runtime/bin/file_win.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#ifndef RUNTIME_BIN_FILE_WIN_H_ -#define RUNTIME_BIN_FILE_WIN_H_ - -#include "file.h" - -// The limit for a regular directory is 248. -// Reference: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation -#define MAX_DIRECTORY_SIZE (MAX_PATH - 12) - -namespace dart { -namespace bin { - -const char* PrefixLongFilePath(const char* path); -const char* PrefixLongDirectoryPath(const char* path); -const char* PrefixLongPathIfExceedLimit(const char* path, bool file_name); - -} // namespace bin -} // namespace dart - -#endif // RUNTIME_BIN_FILE_WIN_H_ diff --git a/runtime/bin/namespace_win.cc b/runtime/bin/namespace_win.cc index 09a05f5db34..f31d37a1db0 100644 --- a/runtime/bin/namespace_win.cc +++ b/runtime/bin/namespace_win.cc @@ -10,8 +10,6 @@ #include #include -#include "bin/file.h" -#include "bin/file_win.h" #include "bin/utils.h" #include "bin/utils_win.h" @@ -48,9 +46,6 @@ const char* Namespace::GetCurrent(Namespace* namespc) { } bool Namespace::SetCurrent(Namespace* namespc, const char* path) { - // TODO(zichangguo): "\\?\" prepended long path doesn't work. - // https://github.com/dart-lang/sdk/issues/42416 - path = PrefixLongDirectoryPath(path); Utf8ToWideScope system_path(path); bool result = SetCurrentDirectoryW(system_path.wide()) != 0; return result; diff --git a/runtime/bin/utils_win.h b/runtime/bin/utils_win.h index 0e4fae9ab81..fdb49097489 100644 --- a/runtime/bin/utils_win.h +++ b/runtime/bin/utils_win.h @@ -9,8 +9,6 @@ #include "platform/utils.h" -#define MAX_LONG_PATH 32767 - namespace dart { namespace bin { diff --git a/tests/standalone/io/file_long_path_test.dart b/tests/standalone/io/file_long_path_test.dart deleted file mode 100644 index 454e633d785..00000000000 --- a/tests/standalone/io/file_long_path_test.dart +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. -// -// This test is Windows-only. - -import 'dart:io'; - -import "package:expect/expect.dart"; - -const maxPath = 260; -const maxDirectoryPath = maxPath - 12; -String longName = '${'x' * 248}'; - -Directory createLongPathDir(Directory tmp) { - if (tmp.path.length <= maxDirectoryPath) { - var path = tmp.path; - path += '\\${'t' * 248}'; - var dir = Directory(path); - dir.createSync(recursive: true); - // Test the rename() of directory - dir = dir.renameSync(tmp.path + '\\$longName'); - Expect.isTrue(dir.existsSync()); - Expect.isFalse(Directory(path).existsSync()); - return dir; - } else { - return tmp; - } -} - -void testCreate(String dir) { - final path = '${dir}\\a_long_path_filename'; - Expect.isTrue(path.length > maxPath); - final file = File(path); - file.createSync(); - Expect.isTrue(file.existsSync()); - file.deleteSync(); -} - -void testCopy(String dir) { - final src = '${dir}\\a_long_path_filename_1'; - final dest = '${dir}\\a_long_path_filename_2'; - Expect.isTrue(src.length > maxPath); - final file1 = File(src); - file1.createSync(); - - final file2 = file1.copySync(dest); - Expect.isTrue(file2.existsSync()); - file1.deleteSync(); - file2.deleteSync(); -} - -void testRename(String dir) { - final path = '${dir}\\a_long_path_filename'; - Expect.isTrue(path.length > maxPath); - final file = File(path); - file.createSync(); - Expect.isTrue(file.existsSync()); - - final renamedFile = file.renameSync('${path}_copy'); - - Expect.isFalse(file.existsSync()); - Expect.isTrue(renamedFile.existsSync()); - renamedFile.deleteSync(); -} - -void testReadWrite(String dir) { - final path = '${dir}\\a_long_path_filename'; - Expect.isTrue(path.length > maxPath); - final file = File(path); - - final content = "testReadWrite"; - file.writeAsStringSync(content); - Expect.isTrue(file.existsSync()); - - int length = file.lengthSync(); - Expect.equals(content.length, length); - - final string = file.readAsStringSync(); - Expect.equals(content, string); - file.deleteSync(); -} - -void testOpen(String dir) { - final path = '${dir}\\a_long_path_filename'; - Expect.isTrue(path.length > maxPath); - final file = File(path); - file.createSync(); - final access = file.openSync(); - access.closeSync(); -} - -void testFileStat(String dir) { - final path = '${dir}\\a_long_path_filename'; - Expect.isTrue(path.length > maxPath); - final file = File(path); - file.createSync(); - final stat = FileStat.statSync(file.path); - - final dateTime = DateTime.utc(2020); - - file.setLastModifiedSync(dateTime); - Expect.notEquals( - stat.modified.toString(), file.lastModifiedSync().toString()); - - file.setLastAccessedSync(dateTime); - Expect.notEquals( - stat.accessed.toString(), file.lastAccessedSync().toString()); -} - -void testCreateLinkToDir(String dir) { - final path = '${dir}\\a_long_path_linkname'; - Expect.isTrue(path.length > maxPath); - var target = '$dir\\a_long_path_target'; - final link = Link(path)..createSync(target); - - final dest = Directory(target)..createSync(); - Expect.isTrue(dest.existsSync()); - - Expect.isTrue(link.existsSync()); - Expect.isTrue(link.targetSync().contains('a_long_path_target')); - - // Rename link - var renamedLink = link.renameSync('${dir}\\a_renamed_long_path_link'); - Expect.isTrue(renamedLink.existsSync()); - Expect.isFalse(link.existsSync()); - Expect.isTrue(renamedLink.targetSync().contains('a_long_path_target')); - - // Update link target - target = '$dir\\an_updated_target'; - final renamedDest = Directory(target)..createSync(); - renamedLink.updateSync(target); - Expect.isTrue(renamedLink.targetSync().contains('an_updated_target')); - - dest.deleteSync(); - renamedDest.deleteSync(); - renamedLink.deleteSync(); -} - -void testCreateLinkToFile(String dir) { - final path = '${dir}\\a_long_path_linkname'; - Expect.isTrue(path.length > maxPath); - var target = '$dir\\a_long_path_target'; - final link = Link(path)..createSync(target); - - final dest = File(target)..createSync(); - Expect.isTrue(dest.existsSync()); - - Expect.isTrue(link.existsSync()); - Expect.isTrue(link.targetSync().contains('a_long_path_target')); - Expect.isTrue(link.resolveSymbolicLinksSync().contains('a_long_path_target')); - - // Rename link - var renamedLink = link.renameSync('${dir}\\a_renamed_long_path_link'); - Expect.isTrue(renamedLink.existsSync()); - Expect.isFalse(link.existsSync()); - Expect.isTrue(renamedLink.targetSync().contains('a_long_path_target')); - - // Update link target - target = '$dir\\an_updated_target'; - final renamedDest = File(target)..createSync(); - renamedLink.updateSync(target); - Expect.isTrue(renamedLink.targetSync().contains('an_updated_target')); - - dest.deleteSync(); - renamedDest.deleteSync(); - renamedLink.deleteSync(); -} - -testNormalLinkToLongPath(String short, String long) { - var target = File('$long\\file_target')..createSync(); - final link = Link('$short\\link')..createSync(target.path); - Expect.isTrue(target.path.length > maxPath); - Expect.isTrue(link.resolveSymbolicLinksSync().length > maxPath); - Expect.isTrue(link.path.length < maxPath); - Expect.isTrue(link.resolveSymbolicLinksSync().contains('file_target')); - - Expect.isTrue(link.existsSync()); - Expect.equals(target.path, link.targetSync()); - - var targetDir = Directory('$long\\dir_target')..createSync(); - link.updateSync(targetDir.path); - Expect.equals(targetDir.path, link.targetSync()); - - link.deleteSync(); - target.deleteSync(); - targetDir.deleteSync(); -} - -testLongPathLinkToNormal(String short, String long) { - var target = File('$short\\file_target')..createSync(); - final link = Link('$long\\link')..createSync(target.path); - - Expect.isTrue(target.path.length < maxPath); - Expect.isTrue(link.path.length > maxPath); - Expect.isTrue(link.resolveSymbolicLinksSync().contains('file_target')); - - Expect.isTrue(link.existsSync()); - Expect.equals(target.path, link.targetSync()); - - var targetDir = Directory('$short\\dir_target')..createSync(); - link.updateSync(targetDir.path); - Expect.equals(targetDir.path, link.targetSync()); - - link.deleteSync(); - target.deleteSync(); - targetDir.deleteSync(); -} - -testDirectorySetCurrent(String dir) { - // This tests setting a long path directory to current directory. - // This will fail. - Expect.isTrue(dir.length > maxPath); - Expect.throws(() { - Directory.current = dir; - }, (e) => e.toString().contains('extension is too long')); -} - -void main() { - if (!Platform.isWindows) { - return; - } - final tmp = Directory.systemTemp.createTempSync('dart-file-long-path'); - final oldCurrent = Directory.current; - Directory.current = tmp; - try { - String dir = createLongPathDir(tmp).path; - testDirectorySetCurrent(dir); - for (final path in [dir, ".\\$longName", ".\\$longName\\..\\$longName"]) { - testCreate(path); - testCopy(path); - testRename(path); - testReadWrite(path); - testOpen(path); - testFileStat(path); - testCreateLinkToDir(path); - testCreateLinkToFile(path); - } - - testNormalLinkToLongPath(tmp.path, dir); - testLongPathLinkToNormal(tmp.path, dir); - } finally { - // Reset the current Directory. - Directory.current = oldCurrent; - tmp.deleteSync(recursive: true); - } -} diff --git a/tests/standalone/io/file_relative_long_path_test.dart b/tests/standalone/io/file_relative_long_path_test.dart deleted file mode 100644 index 0c53ab37a42..00000000000 --- a/tests/standalone/io/file_relative_long_path_test.dart +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. -// -// This test is Windows-only. It tests a short (shorter than 260) relative path -// representing a long absolute path cannot be used by Windows API. Running this -// test without proper support on long path will get an error. - -import 'dart:io'; - -const maxPath = 260; - -void main(args) { - if (!Platform.isWindows) { - return; - } - final dir = Directory.systemTemp.createTempSync('test'); - - if (dir.path.length >= maxPath) { - return; - } - - // Make sure oldpath is shorter than MAX_PATH (260). - int length = (maxPath - dir.path.length) ~/ 2; - final oldpath = Directory('${dir.path}\\${'x' * length}}'); - oldpath.createSync(recursive: true); - final temp = Directory.current; - - Directory.current = oldpath.path; - - // The length of relative path is always shorter than maxPath, but it - // represents a path exceeding the maxPath. - final newpath = Directory('.\\${'y' * 2 * length}'); - newpath.createSync(); - - // Reset current directory before deletion. - Directory.current = temp.path; - dir.deleteSync(recursive: true); -} diff --git a/tests/standalone_2/io/file_long_path_test.dart b/tests/standalone_2/io/file_long_path_test.dart deleted file mode 100644 index 454e633d785..00000000000 --- a/tests/standalone_2/io/file_long_path_test.dart +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. -// -// This test is Windows-only. - -import 'dart:io'; - -import "package:expect/expect.dart"; - -const maxPath = 260; -const maxDirectoryPath = maxPath - 12; -String longName = '${'x' * 248}'; - -Directory createLongPathDir(Directory tmp) { - if (tmp.path.length <= maxDirectoryPath) { - var path = tmp.path; - path += '\\${'t' * 248}'; - var dir = Directory(path); - dir.createSync(recursive: true); - // Test the rename() of directory - dir = dir.renameSync(tmp.path + '\\$longName'); - Expect.isTrue(dir.existsSync()); - Expect.isFalse(Directory(path).existsSync()); - return dir; - } else { - return tmp; - } -} - -void testCreate(String dir) { - final path = '${dir}\\a_long_path_filename'; - Expect.isTrue(path.length > maxPath); - final file = File(path); - file.createSync(); - Expect.isTrue(file.existsSync()); - file.deleteSync(); -} - -void testCopy(String dir) { - final src = '${dir}\\a_long_path_filename_1'; - final dest = '${dir}\\a_long_path_filename_2'; - Expect.isTrue(src.length > maxPath); - final file1 = File(src); - file1.createSync(); - - final file2 = file1.copySync(dest); - Expect.isTrue(file2.existsSync()); - file1.deleteSync(); - file2.deleteSync(); -} - -void testRename(String dir) { - final path = '${dir}\\a_long_path_filename'; - Expect.isTrue(path.length > maxPath); - final file = File(path); - file.createSync(); - Expect.isTrue(file.existsSync()); - - final renamedFile = file.renameSync('${path}_copy'); - - Expect.isFalse(file.existsSync()); - Expect.isTrue(renamedFile.existsSync()); - renamedFile.deleteSync(); -} - -void testReadWrite(String dir) { - final path = '${dir}\\a_long_path_filename'; - Expect.isTrue(path.length > maxPath); - final file = File(path); - - final content = "testReadWrite"; - file.writeAsStringSync(content); - Expect.isTrue(file.existsSync()); - - int length = file.lengthSync(); - Expect.equals(content.length, length); - - final string = file.readAsStringSync(); - Expect.equals(content, string); - file.deleteSync(); -} - -void testOpen(String dir) { - final path = '${dir}\\a_long_path_filename'; - Expect.isTrue(path.length > maxPath); - final file = File(path); - file.createSync(); - final access = file.openSync(); - access.closeSync(); -} - -void testFileStat(String dir) { - final path = '${dir}\\a_long_path_filename'; - Expect.isTrue(path.length > maxPath); - final file = File(path); - file.createSync(); - final stat = FileStat.statSync(file.path); - - final dateTime = DateTime.utc(2020); - - file.setLastModifiedSync(dateTime); - Expect.notEquals( - stat.modified.toString(), file.lastModifiedSync().toString()); - - file.setLastAccessedSync(dateTime); - Expect.notEquals( - stat.accessed.toString(), file.lastAccessedSync().toString()); -} - -void testCreateLinkToDir(String dir) { - final path = '${dir}\\a_long_path_linkname'; - Expect.isTrue(path.length > maxPath); - var target = '$dir\\a_long_path_target'; - final link = Link(path)..createSync(target); - - final dest = Directory(target)..createSync(); - Expect.isTrue(dest.existsSync()); - - Expect.isTrue(link.existsSync()); - Expect.isTrue(link.targetSync().contains('a_long_path_target')); - - // Rename link - var renamedLink = link.renameSync('${dir}\\a_renamed_long_path_link'); - Expect.isTrue(renamedLink.existsSync()); - Expect.isFalse(link.existsSync()); - Expect.isTrue(renamedLink.targetSync().contains('a_long_path_target')); - - // Update link target - target = '$dir\\an_updated_target'; - final renamedDest = Directory(target)..createSync(); - renamedLink.updateSync(target); - Expect.isTrue(renamedLink.targetSync().contains('an_updated_target')); - - dest.deleteSync(); - renamedDest.deleteSync(); - renamedLink.deleteSync(); -} - -void testCreateLinkToFile(String dir) { - final path = '${dir}\\a_long_path_linkname'; - Expect.isTrue(path.length > maxPath); - var target = '$dir\\a_long_path_target'; - final link = Link(path)..createSync(target); - - final dest = File(target)..createSync(); - Expect.isTrue(dest.existsSync()); - - Expect.isTrue(link.existsSync()); - Expect.isTrue(link.targetSync().contains('a_long_path_target')); - Expect.isTrue(link.resolveSymbolicLinksSync().contains('a_long_path_target')); - - // Rename link - var renamedLink = link.renameSync('${dir}\\a_renamed_long_path_link'); - Expect.isTrue(renamedLink.existsSync()); - Expect.isFalse(link.existsSync()); - Expect.isTrue(renamedLink.targetSync().contains('a_long_path_target')); - - // Update link target - target = '$dir\\an_updated_target'; - final renamedDest = File(target)..createSync(); - renamedLink.updateSync(target); - Expect.isTrue(renamedLink.targetSync().contains('an_updated_target')); - - dest.deleteSync(); - renamedDest.deleteSync(); - renamedLink.deleteSync(); -} - -testNormalLinkToLongPath(String short, String long) { - var target = File('$long\\file_target')..createSync(); - final link = Link('$short\\link')..createSync(target.path); - Expect.isTrue(target.path.length > maxPath); - Expect.isTrue(link.resolveSymbolicLinksSync().length > maxPath); - Expect.isTrue(link.path.length < maxPath); - Expect.isTrue(link.resolveSymbolicLinksSync().contains('file_target')); - - Expect.isTrue(link.existsSync()); - Expect.equals(target.path, link.targetSync()); - - var targetDir = Directory('$long\\dir_target')..createSync(); - link.updateSync(targetDir.path); - Expect.equals(targetDir.path, link.targetSync()); - - link.deleteSync(); - target.deleteSync(); - targetDir.deleteSync(); -} - -testLongPathLinkToNormal(String short, String long) { - var target = File('$short\\file_target')..createSync(); - final link = Link('$long\\link')..createSync(target.path); - - Expect.isTrue(target.path.length < maxPath); - Expect.isTrue(link.path.length > maxPath); - Expect.isTrue(link.resolveSymbolicLinksSync().contains('file_target')); - - Expect.isTrue(link.existsSync()); - Expect.equals(target.path, link.targetSync()); - - var targetDir = Directory('$short\\dir_target')..createSync(); - link.updateSync(targetDir.path); - Expect.equals(targetDir.path, link.targetSync()); - - link.deleteSync(); - target.deleteSync(); - targetDir.deleteSync(); -} - -testDirectorySetCurrent(String dir) { - // This tests setting a long path directory to current directory. - // This will fail. - Expect.isTrue(dir.length > maxPath); - Expect.throws(() { - Directory.current = dir; - }, (e) => e.toString().contains('extension is too long')); -} - -void main() { - if (!Platform.isWindows) { - return; - } - final tmp = Directory.systemTemp.createTempSync('dart-file-long-path'); - final oldCurrent = Directory.current; - Directory.current = tmp; - try { - String dir = createLongPathDir(tmp).path; - testDirectorySetCurrent(dir); - for (final path in [dir, ".\\$longName", ".\\$longName\\..\\$longName"]) { - testCreate(path); - testCopy(path); - testRename(path); - testReadWrite(path); - testOpen(path); - testFileStat(path); - testCreateLinkToDir(path); - testCreateLinkToFile(path); - } - - testNormalLinkToLongPath(tmp.path, dir); - testLongPathLinkToNormal(tmp.path, dir); - } finally { - // Reset the current Directory. - Directory.current = oldCurrent; - tmp.deleteSync(recursive: true); - } -} diff --git a/tests/standalone_2/io/file_relative_long_path_test.dart b/tests/standalone_2/io/file_relative_long_path_test.dart deleted file mode 100644 index 0c53ab37a42..00000000000 --- a/tests/standalone_2/io/file_relative_long_path_test.dart +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. -// -// This test is Windows-only. It tests a short (shorter than 260) relative path -// representing a long absolute path cannot be used by Windows API. Running this -// test without proper support on long path will get an error. - -import 'dart:io'; - -const maxPath = 260; - -void main(args) { - if (!Platform.isWindows) { - return; - } - final dir = Directory.systemTemp.createTempSync('test'); - - if (dir.path.length >= maxPath) { - return; - } - - // Make sure oldpath is shorter than MAX_PATH (260). - int length = (maxPath - dir.path.length) ~/ 2; - final oldpath = Directory('${dir.path}\\${'x' * length}}'); - oldpath.createSync(recursive: true); - final temp = Directory.current; - - Directory.current = oldpath.path; - - // The length of relative path is always shorter than maxPath, but it - // represents a path exceeding the maxPath. - final newpath = Directory('.\\${'y' * 2 * length}'); - newpath.createSync(); - - // Reset current directory before deletion. - Directory.current = temp.path; - dir.deleteSync(recursive: true); -}