diff --git a/runtime/bin/file_win.cc b/runtime/bin/file_win.cc index 48af4220dd0..0fb5f6151d3 100644 --- a/runtime/bin/file_win.cc +++ b/runtime/bin/file_win.cc @@ -406,6 +406,10 @@ std::unique_ptr 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 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 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, diff --git a/tests/standalone/io/empty_path_test.dart b/tests/standalone/io/empty_path_test.dart new file mode 100644 index 00000000000..9f81d65efb8 --- /dev/null +++ b/tests/standalone/io/empty_path_test.dart @@ -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 testOpen() async { + try { + await File('').open(); + Expect.equals(1, 0); + } catch (e) { + Expect.equals(1, 1); + } +} + +Future 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 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 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 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 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 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 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 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 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 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 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 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(); +}