Revert "Enable long path on Windows"

This reverts commit 08662f0575.

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 <aam@google.com>
> Commit-Queue: Zichang Guo <zichangguo@google.com>

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 <zichangguo@google.com>
Commit-Queue: Zichang Guo <zichangguo@google.com>
This commit is contained in:
Zichang Guo
2020-09-08 22:34:50 +00:00
committed by commit-bot@chromium.org
parent b8fa8c9052
commit f8fbefd951
11 changed files with 65 additions and 777 deletions
-5
View File
@@ -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`
-1
View File
@@ -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",
+20 -31
View File
@@ -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;
}
+45 -137
View File
@@ -5,11 +5,13 @@
#include "platform/globals.h"
#if defined(HOST_OS_WINDOWS)
#include <Shlwapi.h> // NOLINT
#include "bin/file.h"
#include <WinIoCtl.h> // NOLINT
#include <fcntl.h> // NOLINT
#include <io.h> // NOLINT
#undef StrDup // defined in Shlwapi.h as StrDupW
#include <Shlwapi.h> // NOLINT
#undef StrDup // defined in Shlwapi.h as StrDupW
#include <stdio.h> // NOLINT
#include <string.h> // NOLINT
#include <sys/stat.h> // 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<REPARSE_DATA_BUFFER*>(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<REPARSE_DATA_BUFFER*>(
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<wchar_t*>(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<wchar_t*>(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<char*>(
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(
-24
View File
@@ -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_
-5
View File
@@ -10,8 +10,6 @@
#include <errno.h>
#include <sys/stat.h>
#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;
-2
View File
@@ -9,8 +9,6 @@
#include "platform/utils.h"
#define MAX_LONG_PATH 32767
namespace dart {
namespace bin {
@@ -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<FileSystemException>(() {
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);
}
}
@@ -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);
}
@@ -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<FileSystemException>(() {
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);
}
}
@@ -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);
}