From 97bcd8a631b4096b61d14371b26743331233e343 Mon Sep 17 00:00:00 2001
From: bruvzg <7645683+bruvzg@users.noreply.github.com>
Date: Sat, 7 Oct 2023 14:36:41 +0300
Subject: [PATCH] Add method to check if filesystem is case sensitive.
---
core/io/dir_access.cpp | 6 ++++
core/io/dir_access.h | 2 ++
doc/classes/DirAccess.xml | 8 +++++
drivers/windows/dir_access_windows.cpp | 48 ++++++++++++++++++++++++++
drivers/windows/dir_access_windows.h | 1 +
editor/filesystem_dock.cpp | 12 +++----
platform/macos/dir_access_macos.h | 1 +
platform/macos/dir_access_macos.mm | 17 +++++++++
platform/windows/detect.py | 2 ++
9 files changed, 91 insertions(+), 6 deletions(-)
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp
index 22b63a09290..40c1a539588 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -546,6 +546,10 @@ bool DirAccess::get_include_hidden() const {
return include_hidden;
}
+bool DirAccess::is_case_sensitive(const String &p_path) const {
+ return true;
+}
+
void DirAccess::_bind_methods() {
ClassDB::bind_static_method("DirAccess", D_METHOD("open", "path"), &DirAccess::_open);
ClassDB::bind_static_method("DirAccess", D_METHOD("get_open_error"), &DirAccess::get_open_error);
@@ -583,6 +587,8 @@ void DirAccess::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_include_hidden", "enable"), &DirAccess::set_include_hidden);
ClassDB::bind_method(D_METHOD("get_include_hidden"), &DirAccess::get_include_hidden);
+ ClassDB::bind_method(D_METHOD("is_case_sensitive", "path"), &DirAccess::is_case_sensitive);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_navigational"), "set_include_navigational", "get_include_navigational");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden");
}
diff --git a/core/io/dir_access.h b/core/io/dir_access.h
index 52ed688debf..4ee69571f25 100644
--- a/core/io/dir_access.h
+++ b/core/io/dir_access.h
@@ -159,6 +159,8 @@ public:
void set_include_hidden(bool p_enable);
bool get_include_hidden() const;
+ virtual bool is_case_sensitive(const String &p_path) const;
+
DirAccess() {}
virtual ~DirAccess() {}
};
diff --git a/doc/classes/DirAccess.xml b/doc/classes/DirAccess.xml
index 9c72bc2247e..204bd89aa4c 100644
--- a/doc/classes/DirAccess.xml
+++ b/doc/classes/DirAccess.xml
@@ -204,6 +204,14 @@
Returns the available space on the current directory's disk, in bytes. Returns [code]0[/code] if the platform-specific method to query the available space fails.
+
+
+
+
+ Returns [code]true[/code] if the file system or directory use case sensitive file names.
+ [b]Note:[/b] This method is implemented on macOS and Windows. On other platforms, it always returns [code]true[/code].
+
+
diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp
index 26b8881c39f..8bf83823a03 100644
--- a/drivers/windows/dir_access_windows.cpp
+++ b/drivers/windows/dir_access_windows.cpp
@@ -32,6 +32,7 @@
#include "dir_access_windows.h"
+#include "core/config/project_settings.h"
#include "core/os/memory.h"
#include "core/string/print_string.h"
@@ -40,6 +41,26 @@
#define WIN32_LEAN_AND_MEAN
#include
+typedef struct _NT_IO_STATUS_BLOCK {
+ union {
+ LONG Status;
+ PVOID Pointer;
+ } DUMMY;
+ ULONG_PTR Information;
+} NT_IO_STATUS_BLOCK;
+
+typedef struct _NT_FILE_CASE_SENSITIVE_INFO {
+ ULONG Flags;
+} NT_FILE_CASE_SENSITIVE_INFO;
+
+typedef enum _NT_FILE_INFORMATION_CLASS {
+ FileCaseSensitiveInformation = 71,
+} NT_FILE_INFORMATION_CLASS;
+
+#define NT_FILE_CS_FLAG_CASE_SENSITIVE_DIR 0x00000001
+
+extern "C" NTSYSAPI LONG NTAPI NtQueryInformationFile(HANDLE FileHandle, NT_IO_STATUS_BLOCK *IoStatusBlock, PVOID FileInformation, ULONG Length, NT_FILE_INFORMATION_CLASS FileInformationClass);
+
struct DirAccessWindowsPrivate {
HANDLE h; // handle for FindFirstFile.
WIN32_FIND_DATA f;
@@ -340,6 +361,33 @@ String DirAccessWindows::get_filesystem_type() const {
ERR_FAIL_V("");
}
+bool DirAccessWindows::is_case_sensitive(const String &p_path) const {
+ String f = p_path;
+ if (!f.is_absolute_path()) {
+ f = get_current_dir().path_join(f);
+ }
+ f = fix_path(f);
+
+ HANDLE h_file = ::CreateFileW((LPCWSTR)(f.utf16().get_data()), 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+
+ if (h_file == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ NT_IO_STATUS_BLOCK io_status_block;
+ NT_FILE_CASE_SENSITIVE_INFO file_info;
+ LONG out = NtQueryInformationFile(h_file, &io_status_block, &file_info, sizeof(NT_FILE_CASE_SENSITIVE_INFO), FileCaseSensitiveInformation);
+ ::CloseHandle(h_file);
+
+ if (out >= 0) {
+ return file_info.Flags & NT_FILE_CS_FLAG_CASE_SENSITIVE_DIR;
+ } else {
+ return false;
+ }
+}
+
DirAccessWindows::DirAccessWindows() {
p = memnew(DirAccessWindowsPrivate);
p->h = INVALID_HANDLE_VALUE;
diff --git a/drivers/windows/dir_access_windows.h b/drivers/windows/dir_access_windows.h
index 1e55917756a..1dcab84c9d5 100644
--- a/drivers/windows/dir_access_windows.h
+++ b/drivers/windows/dir_access_windows.h
@@ -84,6 +84,7 @@ public:
uint64_t get_space_left() override;
virtual String get_filesystem_type() const override;
+ virtual bool is_case_sensitive(const String &p_path) const override;
DirAccessWindows();
~DirAccessWindows();
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index be06a3932c3..2bedb090758 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -1776,12 +1776,12 @@ void FileSystemDock::_rename_operation_confirm() {
// Present a more user friendly warning for name conflict.
Ref da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
-#if defined(WINDOWS_ENABLED)
- // Workaround case insensitivity on Windows.
- if ((da->file_exists(new_path) || da->dir_exists(new_path)) && new_path.to_lower() != old_path.to_lower()) {
-#else
- if (da->file_exists(new_path) || da->dir_exists(new_path)) {
-#endif
+
+ bool new_exist = (da->file_exists(new_path) || da->dir_exists(new_path));
+ if (!da->is_case_sensitive(new_path.get_base_dir())) {
+ new_exist = new_exist && (new_path.to_lower() != old_path.to_lower());
+ }
+ if (new_exist) {
EditorNode::get_singleton()->show_warning(TTR("A file or folder with this name already exists."));
s->set_text(col_index, old_name);
return;
diff --git a/platform/macos/dir_access_macos.h b/platform/macos/dir_access_macos.h
index 64556999a78..167c162200d 100644
--- a/platform/macos/dir_access_macos.h
+++ b/platform/macos/dir_access_macos.h
@@ -49,6 +49,7 @@ protected:
virtual String get_drive(int p_drive) override;
virtual bool is_hidden(const String &p_name) override;
+ virtual bool is_case_sensitive(const String &p_path) const override;
};
#endif // UNIX ENABLED
diff --git a/platform/macos/dir_access_macos.mm b/platform/macos/dir_access_macos.mm
index 2413d7bcf34..66d81f2687e 100644
--- a/platform/macos/dir_access_macos.mm
+++ b/platform/macos/dir_access_macos.mm
@@ -30,6 +30,8 @@
#include "dir_access_macos.h"
+#include "core/config/project_settings.h"
+
#if defined(UNIX_ENABLED)
#include
@@ -78,4 +80,19 @@ bool DirAccessMacOS::is_hidden(const String &p_name) {
return [hidden boolValue];
}
+bool DirAccessMacOS::is_case_sensitive(const String &p_path) const {
+ String f = p_path;
+ if (!f.is_absolute_path()) {
+ f = get_current_dir().path_join(f);
+ }
+ f = fix_path(f);
+
+ NSURL *url = [NSURL fileURLWithPath:@(f.utf8().get_data())];
+ NSNumber *cs = nil;
+ if (![url getResourceValue:&cs forKey:NSURLVolumeSupportsCaseSensitiveNamesKey error:nil]) {
+ return false;
+ }
+ return [cs boolValue];
+}
+
#endif // UNIX_ENABLED
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 7caa0153d7b..bdacdbb9ba3 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -419,6 +419,7 @@ def configure_msvc(env, vcvars_msvc_config):
"dwmapi",
"dwrite",
"wbemuuid",
+ "ntdll",
]
if env.debug_features:
@@ -610,6 +611,7 @@ def configure_mingw(env):
"dwmapi",
"dwrite",
"wbemuuid",
+ "ntdll",
]
)