diff options
author | bruvzg <7645683+bruvzg@users.noreply.github.com> | 2024-04-24 10:48:47 +0300 |
---|---|---|
committer | bruvzg <7645683+bruvzg@users.noreply.github.com> | 2024-04-28 19:59:34 +0300 |
commit | fc948e87f6bcbd403b2a5935ceae8b9c42751dfc (patch) | |
tree | 5867a3269148b9255e5a9312a85415097d03673a | |
parent | 6118592c6d88350d01f74faff6fd49754f84a7d0 (diff) | |
download | redot-engine-fc948e87f6bcbd403b2a5935ceae8b9c42751dfc.tar.gz |
Add symlink API support for Windows, expose symlink methods.
-rw-r--r-- | core/io/dir_access.cpp | 4 | ||||
-rw-r--r-- | doc/classes/DirAccess.xml | 26 | ||||
-rw-r--r-- | drivers/windows/dir_access_windows.cpp | 60 | ||||
-rw-r--r-- | drivers/windows/dir_access_windows.h | 6 |
4 files changed, 93 insertions, 3 deletions
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp index e99885befa..5df67b1103 100644 --- a/core/io/dir_access.cpp +++ b/core/io/dir_access.cpp @@ -582,6 +582,10 @@ void DirAccess::_bind_methods() { ClassDB::bind_method(D_METHOD("remove", "path"), &DirAccess::remove); ClassDB::bind_static_method("DirAccess", D_METHOD("remove_absolute", "path"), &DirAccess::remove_absolute); + ClassDB::bind_method(D_METHOD("is_link", "path"), &DirAccess::is_link); + ClassDB::bind_method(D_METHOD("read_link", "path"), &DirAccess::read_link); + ClassDB::bind_method(D_METHOD("create_link", "source", "target"), &DirAccess::create_link); + ClassDB::bind_method(D_METHOD("set_include_navigational", "enable"), &DirAccess::set_include_navigational); ClassDB::bind_method(D_METHOD("get_include_navigational"), &DirAccess::get_include_navigational); ClassDB::bind_method(D_METHOD("set_include_hidden", "enable"), &DirAccess::set_include_hidden); diff --git a/doc/classes/DirAccess.xml b/doc/classes/DirAccess.xml index 03d7f68f43..9c71addf0c 100644 --- a/doc/classes/DirAccess.xml +++ b/doc/classes/DirAccess.xml @@ -94,6 +94,16 @@ Static version of [method copy]. Supports only absolute paths. </description> </method> + <method name="create_link"> + <return type="int" enum="Error" /> + <param index="0" name="source" type="String" /> + <param index="1" name="target" type="String" /> + <description> + Creates symbolic link between files or folders. + [b]Note:[/b] On Windows, this method works only if the application is running with elevated privileges or Developer Mode is enabled. + [b]Note:[/b] This method is implemented on macOS, Linux, and Windows. + </description> + </method> <method name="current_is_dir" qualifiers="const"> <return type="bool" /> <description> @@ -212,6 +222,14 @@ [b]Note:[/b] This method is implemented on macOS, Linux (for EXT4 and F2FS filesystems only) and Windows. On other platforms, it always returns [code]true[/code]. </description> </method> + <method name="is_link"> + <return type="bool" /> + <param index="0" name="path" type="String" /> + <description> + Returns [code]true[/code] if the file or directory is a symbolic link, directory junction, or other reparse point. + [b]Note:[/b] This method is implemented on macOS, Linux, and Windows. + </description> + </method> <method name="list_dir_begin"> <return type="int" enum="Error" /> <description> @@ -264,6 +282,14 @@ Returns [code]null[/code] if opening the directory failed. You can use [method get_open_error] to check the error that occurred. </description> </method> + <method name="read_link"> + <return type="String" /> + <param index="0" name="path" type="String" /> + <description> + Returns target of the symbolic link. + [b]Note:[/b] This method is implemented on macOS, Linux, and Windows. + </description> + </method> <method name="remove"> <return type="int" enum="Error" /> <param index="0" name="path" type="String" /> diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp index 43dd62cdf6..63ba6a6c96 100644 --- a/drivers/windows/dir_access_windows.cpp +++ b/drivers/windows/dir_access_windows.cpp @@ -396,6 +396,66 @@ bool DirAccessWindows::is_case_sensitive(const String &p_path) const { } } +bool DirAccessWindows::is_link(String p_file) { + String f = p_file; + + if (!f.is_absolute_path()) { + f = get_current_dir().path_join(f); + } + f = fix_path(f); + + DWORD attr = GetFileAttributesW((LPCWSTR)(f.utf16().get_data())); + if (attr == INVALID_FILE_ATTRIBUTES) { + return false; + } + + return (attr & FILE_ATTRIBUTE_REPARSE_POINT); +} + +String DirAccessWindows::read_link(String p_file) { + String f = p_file; + + if (!f.is_absolute_path()) { + f = get_current_dir().path_join(f); + } + f = fix_path(f); + + HANDLE hfile = CreateFileW((LPCWSTR)(f.utf16().get_data()), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (hfile == INVALID_HANDLE_VALUE) { + return f; + } + + DWORD ret = GetFinalPathNameByHandleW(hfile, nullptr, 0, VOLUME_NAME_DOS | FILE_NAME_NORMALIZED); + if (ret == 0) { + return f; + } + Char16String cs; + cs.resize(ret + 1); + GetFinalPathNameByHandleW(hfile, (LPWSTR)cs.ptrw(), ret, VOLUME_NAME_DOS | FILE_NAME_NORMALIZED); + CloseHandle(hfile); + + return String::utf16((const char16_t *)cs.ptr(), ret).trim_prefix(R"(\\?\)"); +} + +Error DirAccessWindows::create_link(String p_source, String p_target) { + if (p_target.is_relative_path()) { + p_target = get_current_dir().path_join(p_target); + } + + p_source = fix_path(p_source); + p_target = fix_path(p_target); + + DWORD file_attr = GetFileAttributesW((LPCWSTR)(p_source.utf16().get_data())); + bool is_dir = (file_attr & FILE_ATTRIBUTE_DIRECTORY); + + DWORD flags = ((is_dir) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; + if (CreateSymbolicLinkW((LPCWSTR)p_target.utf16().get_data(), (LPCWSTR)p_source.utf16().get_data(), flags) != 0) { + return OK; + } else { + return FAILED; + } +} + 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 576ba18d9a..46755cbf33 100644 --- a/drivers/windows/dir_access_windows.h +++ b/drivers/windows/dir_access_windows.h @@ -77,9 +77,9 @@ public: virtual Error rename(String p_path, String p_new_path) override; virtual Error remove(String p_path) override; - virtual bool is_link(String p_file) override { return false; }; - virtual String read_link(String p_file) override { return p_file; }; - virtual Error create_link(String p_source, String p_target) override { return FAILED; }; + virtual bool is_link(String p_file) override; + virtual String read_link(String p_file) override; + virtual Error create_link(String p_source, String p_target) override; uint64_t get_space_left() override; |