Skip to content

Commit 08662f0

Browse files
zichanggcommit-bot@chromium.org
authored andcommitted
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: dart-lang/sdk#42416 Change-Id: Ia1b4608d393fb36f1d843858c6f076f3c825dc83 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/152736 Reviewed-by: Alexander Aprelev <[email protected]> Commit-Queue: Zichang Guo <[email protected]>
1 parent 173a958 commit 08662f0

11 files changed

+777
-65
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,14 @@ applications (issue [flutter/flutter#63038][]).
130130
* [Abstract Unix Domain Socket][] is supported on Linux/Android now. Using an
131131
`InternetAddress` with `address` starting with '@' and type being
132132
`InternetAddressType.Unix` will create an abstract Unix Domain Socket.
133+
* On Windows, file APIs can now handle files and directories identified by
134+
long paths (greater than 260 characters). It complies with all restrictions
135+
from [Long Path on Windows][]. Note that `Directory.current` does not work
136+
with long path.
133137

134138
[#42006]: https://github.com/dart-lang/sdk/issues/42006
135139
[Abstract Unix Domain Socket]: http://man7.org/linux/man-pages/man7/unix.7.html
140+
[Long Path on Windows]: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation
136141

137142
#### `dart:html`
138143

runtime/bin/builtin_impl_sources.gni

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ builtin_impl_sources = [
4545
"file_macos.cc",
4646
"file_support.cc",
4747
"file_win.cc",
48+
"file_win.h",
4849
"io_buffer.cc",
4950
"io_buffer.h",
5051
"isolate_data.cc",

runtime/bin/directory_win.cc

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "bin/crypto.h"
1414
#include "bin/dartutils.h"
1515
#include "bin/file.h"
16+
#include "bin/file_win.h"
1617
#include "bin/namespace.h"
1718
#include "bin/utils.h"
1819
#include "bin/utils_win.h"
@@ -21,8 +22,6 @@
2122

2223
#undef DeleteFile
2324

24-
#define MAX_LONG_PATH 32767
25-
2625
namespace dart {
2726
namespace bin {
2827

@@ -278,41 +277,47 @@ static bool DeleteEntry(LPWIN32_FIND_DATAW find_file_data, PathBuffer* path) {
278277
}
279278

280279
static bool DeleteRecursively(PathBuffer* path) {
281-
DWORD attributes = GetFileAttributesW(path->AsStringW());
280+
PathBuffer prefixed_path;
281+
if (!prefixed_path.Add(PrefixLongDirectoryPath(path->AsScopedString()))) {
282+
return false;
283+
}
284+
285+
DWORD attributes = GetFileAttributesW(prefixed_path.AsStringW());
282286
if (attributes == INVALID_FILE_ATTRIBUTES) {
283287
return false;
284288
}
285289
// If the directory is a junction, it's pointing to some other place in the
286290
// filesystem that we do not want to recurse into.
287291
if ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
288292
// Just delete the junction itself.
289-
return RemoveDirectoryW(path->AsStringW()) != 0;
293+
return RemoveDirectoryW(prefixed_path.AsStringW()) != 0;
290294
}
291295
// If it's a file, remove it directly.
292296
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
293-
return DeleteFile(L"", path);
297+
return DeleteFile(L"", &prefixed_path);
294298
}
295299

296-
if (!path->AddW(L"\\*")) {
300+
if (!prefixed_path.AddW(L"\\*")) {
297301
return false;
298302
}
299303

300304
WIN32_FIND_DATAW find_file_data;
301-
HANDLE find_handle = FindFirstFileW(path->AsStringW(), &find_file_data);
305+
HANDLE find_handle =
306+
FindFirstFileW(prefixed_path.AsStringW(), &find_file_data);
302307

303308
if (find_handle == INVALID_HANDLE_VALUE) {
304309
return false;
305310
}
306311

307312
// Adjust the path by removing the '*' used for the search.
308-
int path_length = path->length() - 1;
309-
path->Reset(path_length);
313+
int path_length = prefixed_path.length() - 1;
314+
prefixed_path.Reset(path_length);
310315

311316
do {
312-
if (!DeleteEntry(&find_file_data, path)) {
317+
if (!DeleteEntry(&find_file_data, &prefixed_path)) {
313318
break;
314319
}
315-
path->Reset(path_length); // DeleteEntry adds to the path.
320+
prefixed_path.Reset(path_length); // DeleteEntry adds to the path.
316321
} while (FindNextFileW(find_handle, &find_file_data) != 0);
317322

318323
DWORD last_error = GetLastError();
@@ -324,8 +329,9 @@ static bool DeleteRecursively(PathBuffer* path) {
324329
return false;
325330
}
326331
// All content deleted succesfully, try to delete directory.
327-
path->Reset(path_length - 1); // Drop the "\" from the end of the path.
328-
return RemoveDirectoryW(path->AsStringW()) != 0;
332+
prefixed_path.Reset(path_length -
333+
1); // Drop the "\" from the end of the path.
334+
return RemoveDirectoryW(prefixed_path.AsStringW()) != 0;
329335
}
330336

331337
static Directory::ExistsResult ExistsHelper(const wchar_t* dir_name) {
@@ -349,7 +355,8 @@ static Directory::ExistsResult ExistsHelper(const wchar_t* dir_name) {
349355

350356
Directory::ExistsResult Directory::Exists(Namespace* namespc,
351357
const char* dir_name) {
352-
Utf8ToWideScope system_name(dir_name);
358+
const char* prefixed_dir_name = PrefixLongDirectoryPath(dir_name);
359+
Utf8ToWideScope system_name(prefixed_dir_name);
353360
return ExistsHelper(system_name.wide());
354361
}
355362

@@ -369,7 +376,8 @@ char* Directory::CurrentNoScope() {
369376
}
370377

371378
bool Directory::Create(Namespace* namespc, const char* dir_name) {
372-
Utf8ToWideScope system_name(dir_name);
379+
const char* prefixed_dir_name = PrefixLongDirectoryPath(dir_name);
380+
Utf8ToWideScope system_name(prefixed_dir_name);
373381
int create_status = CreateDirectoryW(system_name.wide(), NULL);
374382
// If the directory already existed, treat it as a success.
375383
if ((create_status == 0) && (GetLastError() == ERROR_ALREADY_EXISTS) &&
@@ -475,10 +483,11 @@ const char* Directory::CreateTemp(Namespace* namespc, const char* prefix) {
475483
bool Directory::Delete(Namespace* namespc,
476484
const char* dir_name,
477485
bool recursive) {
486+
const char* prefixed_dir_name = PrefixLongDirectoryPath(dir_name);
478487
bool result = false;
479-
Utf8ToWideScope system_dir_name(dir_name);
488+
Utf8ToWideScope system_dir_name(prefixed_dir_name);
480489
if (!recursive) {
481-
if (File::GetType(namespc, dir_name, true) == File::kIsDirectory) {
490+
if (File::GetType(namespc, prefixed_dir_name, true) == File::kIsDirectory) {
482491
result = (RemoveDirectoryW(system_dir_name.wide()) != 0);
483492
} else {
484493
SetLastError(ERROR_FILE_NOT_FOUND);
@@ -495,18 +504,20 @@ bool Directory::Delete(Namespace* namespc,
495504
bool Directory::Rename(Namespace* namespc,
496505
const char* path,
497506
const char* new_path) {
498-
Utf8ToWideScope system_path(path);
499-
Utf8ToWideScope system_new_path(new_path);
507+
const char* prefixed_dir = PrefixLongDirectoryPath(path);
508+
Utf8ToWideScope system_path(prefixed_dir);
500509
ExistsResult exists = ExistsHelper(system_path.wide());
501510
if (exists != EXISTS) {
502511
return false;
503512
}
513+
const char* prefixed_new_dir = PrefixLongDirectoryPath(new_path);
514+
Utf8ToWideScope system_new_path(prefixed_new_dir);
504515
ExistsResult new_exists = ExistsHelper(system_new_path.wide());
505516
// MoveFile does not allow replacing existing directories. Therefore,
506517
// if the new_path is currently a directory we need to delete it
507518
// first.
508519
if (new_exists == EXISTS) {
509-
bool success = Delete(namespc, new_path, true);
520+
bool success = Delete(namespc, prefixed_new_dir, true);
510521
if (!success) {
511522
return false;
512523
}

0 commit comments

Comments
 (0)