Fix for App crash when File class receive an empty string
TEST=new test added Change-Id: I575d85c5ab13a2094114a6fa2a7fe9f06ebeabe4 FIXES: https://github.com/dart-lang/sdk/issues/60331 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/417962 Reviewed-by: Brian Quinlan <bquinlan@google.com>
This commit is contained in:
+58
-5
@@ -406,6 +406,10 @@ std::unique_ptr<wchar_t[]> ToWinAPIPath(const char* utf8_path) {
|
||||
|
||||
File* File::Open(Namespace* namespc, const char* name, FileOpenMode mode) {
|
||||
const auto path = ToWinAPIPath(name);
|
||||
if (path.get() == nullptr) {
|
||||
SetLastError(ERROR_INVALID_NAME);
|
||||
return nullptr;
|
||||
}
|
||||
File* file = FileOpenW(path.get(), mode);
|
||||
return file;
|
||||
}
|
||||
@@ -476,6 +480,9 @@ static bool FileExists(const wchar_t* path) {
|
||||
|
||||
bool File::Exists(Namespace* namespc, const char* name) {
|
||||
const auto path = ToWinAPIPath(name);
|
||||
if (path.get() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return FileExists(path.get());
|
||||
}
|
||||
|
||||
@@ -490,6 +497,9 @@ bool File::ExistsUri(Namespace* namespc, const char* uri) {
|
||||
|
||||
bool File::Create(Namespace* namespc, const char* name, bool exclusive) {
|
||||
const auto path = ToWinAPIPath(name);
|
||||
if (path.get() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
int flags = O_RDONLY | O_CREAT;
|
||||
if (exclusive) {
|
||||
flags |= O_EXCL;
|
||||
@@ -535,11 +545,19 @@ bool File::CreateLink(Namespace* namespc,
|
||||
const char* utf8_name,
|
||||
const char* utf8_target) {
|
||||
const auto name = ToWinAPIPath(utf8_name);
|
||||
if (name.get() == nullptr) {
|
||||
SetLastError(ERROR_INVALID_NAME);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<wchar_t[]> target;
|
||||
bool target_is_directory;
|
||||
if (File::IsAbsolutePath(utf8_target)) {
|
||||
target = ToWinAPIPath(utf8_target);
|
||||
if (target.get() == nullptr) {
|
||||
SetLastError(ERROR_INVALID_NAME);
|
||||
return false;
|
||||
}
|
||||
target_is_directory =
|
||||
File::GetType(target.get(), /*follow_links=*/true) == kIsDirectory;
|
||||
} else {
|
||||
@@ -625,6 +643,9 @@ bool File::CreatePipe(Namespace* namespc, File** readPipe, File** writePipe) {
|
||||
|
||||
bool File::Delete(Namespace* namespc, const char* name) {
|
||||
const auto path = ToWinAPIPath(name);
|
||||
if (path.get() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
int status = _wremove(path.get());
|
||||
return status != -1;
|
||||
}
|
||||
@@ -650,6 +671,9 @@ static bool DeleteLinkHelper(const wchar_t* path) {
|
||||
|
||||
bool File::DeleteLink(Namespace* namespc, const char* name) {
|
||||
const auto path = ToWinAPIPath(name);
|
||||
if (path.get() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return DeleteLinkHelper(path.get());
|
||||
}
|
||||
|
||||
@@ -657,12 +681,20 @@ static bool RenameHelper(File::Type expected,
|
||||
const char* old_name,
|
||||
const char* new_name) {
|
||||
const auto old_path = ToWinAPIPath(old_name);
|
||||
if (old_path.get() == nullptr) {
|
||||
SetLastError(ERROR_INVALID_NAME);
|
||||
return false;
|
||||
}
|
||||
File::Type type = File::GetType(old_path.get(), /*follow_links=*/false);
|
||||
if (type != expected) {
|
||||
SetLastError(ERROR_FILE_NOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
const auto new_path = ToWinAPIPath(new_name);
|
||||
if (new_path.get() == nullptr) {
|
||||
SetLastError(ERROR_INVALID_NAME);
|
||||
return false;
|
||||
}
|
||||
DWORD flags = MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING;
|
||||
|
||||
// Symbolic links (e.g. produced by Link.create) to directories on Windows
|
||||
@@ -780,6 +812,10 @@ bool File::Copy(Namespace* namespc,
|
||||
// CopyIntoTempFile so we force long prefix no matter what.
|
||||
const auto old_path = ToWinAPIPath(old_name);
|
||||
const auto new_path = ToWinAPIPath(new_name);
|
||||
if (new_path.get() == nullptr || old_path.get() == nullptr) {
|
||||
SetLastError(ERROR_INVALID_NAME);
|
||||
return false;
|
||||
}
|
||||
|
||||
File::Type type = GetType(old_path.get(), /*follow_links=*/false);
|
||||
if (type != kIsFile) {
|
||||
@@ -812,7 +848,7 @@ bool File::Copy(Namespace* namespc,
|
||||
int64_t File::LengthFromPath(Namespace* namespc, const char* name) {
|
||||
struct __stat64 st;
|
||||
const auto path = ToWinAPIPath(name);
|
||||
if (!StatHelper(path.get(), &st)) {
|
||||
if (path.get() == nullptr || !StatHelper(path.get(), &st)) {
|
||||
return -1;
|
||||
}
|
||||
return st.st_size;
|
||||
@@ -823,6 +859,9 @@ const char* File::LinkTarget(Namespace* namespc,
|
||||
char* dest,
|
||||
int dest_size) {
|
||||
const auto path = ToWinAPIPath(pathname);
|
||||
if (path.get() == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
HANDLE dir_handle = CreateFileW(
|
||||
path.get(), GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
|
||||
@@ -910,6 +949,10 @@ const char* File::LinkTarget(Namespace* namespc,
|
||||
|
||||
void File::Stat(Namespace* namespc, const char* name, int64_t* data) {
|
||||
const auto path = ToWinAPIPath(name);
|
||||
if (path.get() == nullptr) {
|
||||
data[kType] = File::kDoesNotExist;
|
||||
return;
|
||||
}
|
||||
File::Type type = GetType(path.get(), /*follow_links=*/true);
|
||||
data[kType] = type;
|
||||
if (type != kDoesNotExist) {
|
||||
@@ -930,7 +973,7 @@ void File::Stat(Namespace* namespc, const char* name, int64_t* data) {
|
||||
time_t File::LastAccessed(Namespace* namespc, const char* name) {
|
||||
struct __stat64 st;
|
||||
const auto path = ToWinAPIPath(name);
|
||||
if (!StatHelper(path.get(), &st)) {
|
||||
if (path.get() == nullptr || !StatHelper(path.get(), &st)) {
|
||||
return -1;
|
||||
}
|
||||
return st.st_atime;
|
||||
@@ -939,7 +982,7 @@ time_t File::LastAccessed(Namespace* namespc, const char* name) {
|
||||
time_t File::LastModified(Namespace* namespc, const char* name) {
|
||||
struct __stat64 st;
|
||||
const auto path = ToWinAPIPath(name);
|
||||
if (!StatHelper(path.get(), &st)) {
|
||||
if (path.get() == nullptr || !StatHelper(path.get(), &st)) {
|
||||
return -1;
|
||||
}
|
||||
return st.st_mtime;
|
||||
@@ -950,7 +993,7 @@ bool File::SetLastAccessed(Namespace* namespc,
|
||||
int64_t millis) {
|
||||
struct __stat64 st;
|
||||
const auto path = ToWinAPIPath(name);
|
||||
if (!StatHelper(path.get(), &st)) { // Checks that it is a file.
|
||||
if (path.get() == nullptr || !StatHelper(path.get(), &st)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -979,7 +1022,7 @@ bool File::SetLastModified(Namespace* namespc,
|
||||
// First get the current times.
|
||||
struct __stat64 st;
|
||||
const auto path = ToWinAPIPath(name);
|
||||
if (!StatHelper(path.get(), &st)) {
|
||||
if (path.get() == nullptr || !StatHelper(path.get(), &st)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1008,6 +1051,10 @@ const char* File::GetCanonicalPath(Namespace* namespc,
|
||||
char* dest,
|
||||
int dest_size) {
|
||||
const auto path = ToWinAPIPath(pathname);
|
||||
if (path.get() == nullptr) {
|
||||
SetLastError(ERROR_INVALID_NAME);
|
||||
return nullptr;
|
||||
}
|
||||
HANDLE file_handle =
|
||||
CreateFileW(path.get(), 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, nullptr);
|
||||
@@ -1110,6 +1157,9 @@ File::Type File::GetType(Namespace* namespc,
|
||||
const char* name,
|
||||
bool follow_links) {
|
||||
const auto path = ToWinAPIPath(name);
|
||||
if (path.get() == nullptr) {
|
||||
return File::kDoesNotExist;
|
||||
}
|
||||
return GetType(path.get(), follow_links);
|
||||
}
|
||||
|
||||
@@ -1122,6 +1172,9 @@ File::Identical File::AreIdentical(Namespace* namespc_1,
|
||||
BY_HANDLE_FILE_INFORMATION file_info[2];
|
||||
const std::unique_ptr<wchar_t[]> file_names[2] = {ToWinAPIPath(file_1),
|
||||
ToWinAPIPath(file_2)};
|
||||
if (file_names[0].get() == nullptr || file_names[1].get() == nullptr) {
|
||||
return File::kError;
|
||||
}
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
HANDLE file_handle = CreateFileW(
|
||||
file_names[i].get(), 0,
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
// Copyright (c) 2025, 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.
|
||||
//
|
||||
// Dart test program for testing empty file pathname parameter.
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import "package:expect/async_helper.dart";
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
Future<void> testOpen() async {
|
||||
try {
|
||||
await File('').open();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testExists() async {
|
||||
try {
|
||||
var retval = await File('').exists();
|
||||
Expect.equals(retval, false);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 0);
|
||||
}
|
||||
try {
|
||||
var retval = File('').existsSync();
|
||||
Expect.equals(retval, false);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testCreate() async {
|
||||
try {
|
||||
await File('').create();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
File('').createSync();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testCreateLink() async {
|
||||
try {
|
||||
await Link('').create('test');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
await Link('test').create('');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
Link('').createSync('test');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
Link('test').createSync('');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testDelete() async {
|
||||
try {
|
||||
await File('').delete();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
File('').deleteSync();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testDeleteLink() async {
|
||||
try {
|
||||
await Link('').delete();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
Link('').deleteSync();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testRename() async {
|
||||
try {
|
||||
await File('').rename('test');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
late File file;
|
||||
Directory directory = Directory.systemTemp.createTempSync(
|
||||
'dart_test_directory',
|
||||
);
|
||||
try {
|
||||
file = File('${directory.path}/test');
|
||||
file.createSync();
|
||||
await file.rename('');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
} finally {
|
||||
file.deleteSync();
|
||||
}
|
||||
try {
|
||||
File('').renameSync('test');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
file = File('${directory.path}/test');
|
||||
file.createSync();
|
||||
file.renameSync('');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
} finally {
|
||||
file.deleteSync();
|
||||
}
|
||||
directory.deleteSync(recursive: true);
|
||||
}
|
||||
|
||||
Future<void> testCopy() async {
|
||||
try {
|
||||
await File('').copy('test');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
await File('test').copy('');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
File('').copySync('test');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
File('test').copySync('');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testLength() async {
|
||||
try {
|
||||
var len = await File('').length();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
var len = File('').lengthSync();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testStat() async {
|
||||
final now = DateTime.now();
|
||||
try {
|
||||
var len = await File('').stat();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
var len = File('').statSync();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
var len = await File('').lastAccessed();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
var len = File('').lastAccessedSync();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
var len = await File('').setLastAccessed(now);
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
var len = File('').setLastAccessedSync(now);
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
var len = await File('').lastModified();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
var len = File('').lastModifiedSync();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
var len = await File('').setLastModified(now);
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
var len = File('').setLastModifiedSync(now);
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testResolveSymbolicLinks() async {
|
||||
try {
|
||||
var str = await File('').resolveSymbolicLinks();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
var str = File('').resolveSymbolicLinksSync();
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testType() async {
|
||||
try {
|
||||
var val = await FileSystemEntity.isDirectory('');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
var val = FileSystemEntity.isDirectorySync('');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testIdentical() async {
|
||||
try {
|
||||
var val = await FileSystemEntity.identical('', 'test');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
try {
|
||||
var val = FileSystemEntity.identicalSync('test', '');
|
||||
Expect.equals(1, 0);
|
||||
} catch (e) {
|
||||
Expect.equals(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
main() async {
|
||||
await testOpen();
|
||||
await testExists();
|
||||
await testCreate();
|
||||
await testCreateLink();
|
||||
await testDelete();
|
||||
await testDeleteLink();
|
||||
await testRename();
|
||||
await testCopy();
|
||||
await testLength();
|
||||
await testStat();
|
||||
await testResolveSymbolicLinks();
|
||||
await testType();
|
||||
await testIdentical();
|
||||
}
|
||||
Reference in New Issue
Block a user