diff --git a/Utilities/File.cpp b/Utilities/File.cpp index d075b9c9c3..85f90a72bb 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -352,16 +352,57 @@ bool fs::stat(const std::string& path, stat_t& info) } #ifdef _WIN32 - WIN32_FILE_ATTRIBUTE_DATA attrs; - if (!GetFileAttributesExW(to_wchar(path).get(), GetFileExInfoStandard, &attrs)) + WIN32_FIND_DATA attrs; + std::string_view epath = path; + + // '/' and '\\' Not allowed by FindFirstFileExW at the end of path but we should allow it + if (auto not_del = epath.find_last_not_of("/\\"); not_del != umax && not_del != epath.size() - 1) + { + epath.remove_suffix(path.size() - 1 - not_del); + } + + // Allowed by FindFirstFileExW but we should not allow it + if (epath.ends_with("*")) + { + g_tls_error = fs::error::noent; + return false; + } + + const auto wchar_ptr = to_wchar(std::string(epath)); + const std::wstring_view wpath_view = wchar_ptr.get(); + + const HANDLE handle = FindFirstFileExW(wpath_view.data(), FindExInfoStandard, &attrs, FindExSearchNameMatch, nullptr, FIND_FIRST_EX_CASE_SENSITIVE); + + if (handle == INVALID_HANDLE_VALUE) { g_tls_error = to_error(GetLastError()); return false; } + struct close_t + { + HANDLE handle; + ~close_t() { FindClose(handle); } + }; + + for (close_t find_manage{handle}; attrs.cFileName != wpath_view.substr(wpath_view.find_last_of(L"/\\") + 1);) + { + if (!FindNextFileW(handle, &attrs)) + { + if (const DWORD err = GetLastError(); err != ERROR_NO_MORE_FILES) + { + g_tls_error = to_error(err); + return false; + } + + g_tls_error = fs::error::noent; + return false; + } + } + info.is_directory = (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; info.is_writable = (attrs.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0; - info.size = (u64)attrs.nFileSizeLow | ((u64)attrs.nFileSizeHigh << 32); + info.size = attrs.nFileSizeLow | (u64{attrs.nFileSizeHigh} << 32); info.atime = to_time(attrs.ftLastAccessTime); info.mtime = to_time(attrs.ftLastWriteTime); info.ctime = info.mtime; @@ -389,73 +430,19 @@ bool fs::stat(const std::string& path, stat_t& info) bool fs::exists(const std::string& path) { - if (auto device = get_virtual_device(path)) - { - stat_t info; - return device->stat(path, info); - } - -#ifdef _WIN32 - if (GetFileAttributesW(to_wchar(path).get()) == INVALID_FILE_ATTRIBUTES) - { - g_tls_error = to_error(GetLastError()); - return false; - } - - return true; -#else - struct ::stat file_info; - if (::stat(path.c_str(), &file_info) != 0) - { - g_tls_error = to_error(errno); - return false; - } - - return true; -#endif + fs::stat_t info{}; + return fs::stat(path, info); } bool fs::is_file(const std::string& path) { - if (auto device = get_virtual_device(path)) + fs::stat_t info{}; + if (!fs::stat(path, info)) { - stat_t info; - if (!device->stat(path, info)) - { - return false; - } - - if (info.is_directory) - { - g_tls_error = error::exist; - return false; - } - - return true; - } - -#ifdef _WIN32 - const DWORD attrs = GetFileAttributesW(to_wchar(path).get()); - if (attrs == INVALID_FILE_ATTRIBUTES) - { - g_tls_error = to_error(GetLastError()); return false; } -#else - struct ::stat file_info; - if (::stat(path.c_str(), &file_info) != 0) - { - g_tls_error = to_error(errno); - return false; - } -#endif - // TODO: correct file type check -#ifdef _WIN32 - if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0) -#else - if (S_ISDIR(file_info.st_mode)) -#endif + if (info.is_directory) { g_tls_error = error::exist; return false; @@ -466,44 +453,13 @@ bool fs::is_file(const std::string& path) bool fs::is_dir(const std::string& path) { - if (auto device = get_virtual_device(path)) + fs::stat_t info{}; + if (!fs::stat(path, info)) { - stat_t info; - if (!device->stat(path, info)) - { - return false; - } - - if (info.is_directory == false) - { - g_tls_error = error::exist; - return false; - } - - return true; - } - -#ifdef _WIN32 - const DWORD attrs = GetFileAttributesW(to_wchar(path).get()); - if (attrs == INVALID_FILE_ATTRIBUTES) - { - g_tls_error = to_error(GetLastError()); return false; } -#else - struct ::stat file_info; - if (::stat(path.c_str(), &file_info) != 0) - { - g_tls_error = to_error(errno); - return false; - } -#endif -#ifdef _WIN32 - if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0) -#else - if (!S_ISDIR(file_info.st_mode)) -#endif + if (!info.is_directory) { g_tls_error = error::exist; return false;