Merge pull request #82957 from bruvzg/fs_case_check

Add method to check if filesystem is case sensitive.
This commit is contained in:
Rémi Verschelde 2023-10-13 12:55:35 +02:00
commit 51f81e1c88
No known key found for this signature in database
GPG Key ID: C3336907360768E1
9 changed files with 91 additions and 6 deletions

View File

@ -546,6 +546,10 @@ bool DirAccess::get_include_hidden() const {
return include_hidden; return include_hidden;
} }
bool DirAccess::is_case_sensitive(const String &p_path) const {
return true;
}
void DirAccess::_bind_methods() { void DirAccess::_bind_methods() {
ClassDB::bind_static_method("DirAccess", D_METHOD("open", "path"), &DirAccess::_open); 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); 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("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("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_navigational"), "set_include_navigational", "get_include_navigational");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden");
} }

View File

@ -159,6 +159,8 @@ public:
void set_include_hidden(bool p_enable); void set_include_hidden(bool p_enable);
bool get_include_hidden() const; bool get_include_hidden() const;
virtual bool is_case_sensitive(const String &p_path) const;
DirAccess() {} DirAccess() {}
virtual ~DirAccess() {} virtual ~DirAccess() {}
}; };

View File

@ -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 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.
</description> </description>
</method> </method>
<method name="is_case_sensitive" qualifiers="const">
<return type="bool" />
<param index="0" name="path" type="String" />
<description>
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].
</description>
</method>
<method name="list_dir_begin"> <method name="list_dir_begin">
<return type="int" enum="Error" /> <return type="int" enum="Error" />
<description> <description>

View File

@ -32,6 +32,7 @@
#include "dir_access_windows.h" #include "dir_access_windows.h"
#include "core/config/project_settings.h"
#include "core/os/memory.h" #include "core/os/memory.h"
#include "core/string/print_string.h" #include "core/string/print_string.h"
@ -40,6 +41,26 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
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 { struct DirAccessWindowsPrivate {
HANDLE h; // handle for FindFirstFile. HANDLE h; // handle for FindFirstFile.
WIN32_FIND_DATA f; WIN32_FIND_DATA f;
@ -340,6 +361,33 @@ String DirAccessWindows::get_filesystem_type() const {
ERR_FAIL_V(""); 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() { DirAccessWindows::DirAccessWindows() {
p = memnew(DirAccessWindowsPrivate); p = memnew(DirAccessWindowsPrivate);
p->h = INVALID_HANDLE_VALUE; p->h = INVALID_HANDLE_VALUE;

View File

@ -84,6 +84,7 @@ public:
uint64_t get_space_left() override; uint64_t get_space_left() override;
virtual String get_filesystem_type() const override; virtual String get_filesystem_type() const override;
virtual bool is_case_sensitive(const String &p_path) const override;
DirAccessWindows(); DirAccessWindows();
~DirAccessWindows(); ~DirAccessWindows();

View File

@ -1776,12 +1776,12 @@ void FileSystemDock::_rename_operation_confirm() {
// Present a more user friendly warning for name conflict. // Present a more user friendly warning for name conflict.
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
#if defined(WINDOWS_ENABLED)
// Workaround case insensitivity on Windows. bool new_exist = (da->file_exists(new_path) || da->dir_exists(new_path));
if ((da->file_exists(new_path) || da->dir_exists(new_path)) && new_path.to_lower() != old_path.to_lower()) { if (!da->is_case_sensitive(new_path.get_base_dir())) {
#else new_exist = new_exist && (new_path.to_lower() != old_path.to_lower());
if (da->file_exists(new_path) || da->dir_exists(new_path)) { }
#endif if (new_exist) {
EditorNode::get_singleton()->show_warning(TTR("A file or folder with this name already exists.")); EditorNode::get_singleton()->show_warning(TTR("A file or folder with this name already exists."));
ti->set_text(col_index, old_name); ti->set_text(col_index, old_name);
return; return;

View File

@ -49,6 +49,7 @@ protected:
virtual String get_drive(int p_drive) override; virtual String get_drive(int p_drive) override;
virtual bool is_hidden(const String &p_name) override; virtual bool is_hidden(const String &p_name) override;
virtual bool is_case_sensitive(const String &p_path) const override;
}; };
#endif // UNIX ENABLED #endif // UNIX ENABLED

View File

@ -30,6 +30,8 @@
#include "dir_access_macos.h" #include "dir_access_macos.h"
#include "core/config/project_settings.h"
#if defined(UNIX_ENABLED) #if defined(UNIX_ENABLED)
#include <errno.h> #include <errno.h>
@ -78,4 +80,19 @@ bool DirAccessMacOS::is_hidden(const String &p_name) {
return [hidden boolValue]; 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 #endif // UNIX_ENABLED

View File

@ -419,6 +419,7 @@ def configure_msvc(env, vcvars_msvc_config):
"dwmapi", "dwmapi",
"dwrite", "dwrite",
"wbemuuid", "wbemuuid",
"ntdll",
] ]
if env.debug_features: if env.debug_features:
@ -610,6 +611,7 @@ def configure_mingw(env):
"dwmapi", "dwmapi",
"dwrite", "dwrite",
"wbemuuid", "wbemuuid",
"ntdll",
] ]
) )