From 6aa8f7d85b2facd59ea43c6f76f1a6d35e833cb0 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Wed, 10 Mar 2021 10:29:43 +0200 Subject: [PATCH] Add symlink API to the DirAccess (on macOS and Linux). --- core/io/file_access_pack.h | 4 +++ core/os/dir_access.cpp | 12 ++++--- core/os/dir_access.h | 8 +++-- drivers/unix/dir_access_unix.cpp | 43 ++++++++++++++++++++++++++ drivers/unix/dir_access_unix.h | 4 +++ drivers/windows/dir_access_windows.h | 5 +++ platform/android/dir_access_jandroid.h | 4 +++ 7 files changed, 73 insertions(+), 7 deletions(-) diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 921eb74a847..4864d97d83e 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -235,6 +235,10 @@ public: virtual Error rename(String p_from, String p_to); virtual Error remove(String p_name); + virtual bool is_link(String p_file) { return false; } + virtual String read_link(String p_file) { return p_file; } + virtual Error create_link(String p_source, String p_target) { return FAILED; } + uint64_t get_space_left(); virtual String get_filesystem_type() const; diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp index 9036ee28247..35408361e4e 100644 --- a/core/os/dir_access.cpp +++ b/core/os/dir_access.cpp @@ -334,7 +334,7 @@ public: } }; -Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags) { +Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags, bool p_copy_links) { List dirs; String curdir = get_current_dir(); @@ -342,7 +342,9 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag String n = get_next(); while (n != String()) { if (n != "." && n != "..") { - if (current_is_dir()) { + if (p_copy_links && is_link(get_current_dir().plus_file(n))) { + create_link(read_link(get_current_dir().plus_file(n)), p_to + n); + } else if (current_is_dir()) { dirs.push_back(n); } else { const String &rel_path = n; @@ -374,7 +376,7 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag Error err = change_dir(E->get()); ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + E->get() + "'."); - err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags); + err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags, p_copy_links); if (err) { change_dir(".."); ERR_FAIL_V_MSG(err, "Failed to copy recursively."); @@ -386,7 +388,7 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag return OK; } -Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags) { +Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags, bool p_copy_links) { ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist."); DirAccess *target_da = DirAccess::create_for_path(p_to); @@ -405,7 +407,7 @@ Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags) { } DirChanger dir_changer(this, p_from); - Error err = _copy_dir(target_da, p_to, p_chmod_flags); + Error err = _copy_dir(target_da, p_to, p_chmod_flags, p_copy_links); memdelete(target_da); return err; diff --git a/core/os/dir_access.h b/core/os/dir_access.h index 6c12bef2157..3df949e35b6 100644 --- a/core/os/dir_access.h +++ b/core/os/dir_access.h @@ -50,7 +50,7 @@ private: AccessType _access_type; static CreateFunc create_func[ACCESS_MAX]; ///< set this to instance a filesystem object - Error _copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags); + Error _copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags, bool p_copy_links); protected: String _get_root_path() const; @@ -89,11 +89,15 @@ public: static bool exists(String p_dir); virtual uint64_t get_space_left() = 0; - Error copy_dir(String p_from, String p_to, int p_chmod_flags = -1); + Error copy_dir(String p_from, String p_to, int p_chmod_flags = -1, bool p_copy_links = false); virtual Error copy(String p_from, String p_to, int p_chmod_flags = -1); virtual Error rename(String p_from, String p_to) = 0; virtual Error remove(String p_name) = 0; + virtual bool is_link(String p_file) = 0; + virtual String read_link(String p_file) = 0; + virtual Error create_link(String p_source, String p_target) = 0; + // Meant for editor code when we want to quickly remove a file without custom // handling (e.g. removing a cache file). static void remove_file_or_error(String p_path) { diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp index c9c15cb8b8d..6ac491f6d0f 100644 --- a/drivers/unix/dir_access_unix.cpp +++ b/drivers/unix/dir_access_unix.cpp @@ -382,6 +382,49 @@ Error DirAccessUnix::remove(String p_path) { } } +bool DirAccessUnix::is_link(String p_file) { + if (p_file.is_rel_path()) + p_file = get_current_dir().plus_file(p_file); + + p_file = fix_path(p_file); + + struct stat flags; + if ((lstat(p_file.utf8().get_data(), &flags) != 0)) + return FAILED; + + return S_ISLNK(flags.st_mode); +} + +String DirAccessUnix::read_link(String p_file) { + if (p_file.is_rel_path()) + p_file = get_current_dir().plus_file(p_file); + + p_file = fix_path(p_file); + + char buf[256]; + memset(buf, 0, 256); + ssize_t len = readlink(p_file.utf8().get_data(), buf, sizeof(buf)); + String link; + if (len > 0) { + link.parse_utf8(buf, len); + } + return link; +} + +Error DirAccessUnix::create_link(String p_source, String p_target) { + if (p_target.is_rel_path()) + p_target = get_current_dir().plus_file(p_target); + + p_source = fix_path(p_source); + p_target = fix_path(p_target); + + if (symlink(p_source.utf8().get_data(), p_target.utf8().get_data()) == 0) { + return OK; + } else { + return FAILED; + } +} + uint64_t DirAccessUnix::get_space_left() { #ifndef NO_STATVFS struct statvfs vfs; diff --git a/drivers/unix/dir_access_unix.h b/drivers/unix/dir_access_unix.h index d7f274c9b4c..ba91753697e 100644 --- a/drivers/unix/dir_access_unix.h +++ b/drivers/unix/dir_access_unix.h @@ -77,6 +77,10 @@ public: virtual Error rename(String p_path, String p_new_path); virtual Error remove(String p_path); + virtual bool is_link(String p_file); + virtual String read_link(String p_file); + virtual Error create_link(String p_source, String p_target); + virtual uint64_t get_space_left(); virtual String get_filesystem_type() const; diff --git a/drivers/windows/dir_access_windows.h b/drivers/windows/dir_access_windows.h index 930248e6122..037f3b62b47 100644 --- a/drivers/windows/dir_access_windows.h +++ b/drivers/windows/dir_access_windows.h @@ -79,6 +79,11 @@ public: virtual Error rename(String p_path, String p_new_path); virtual Error remove(String p_path); + virtual bool is_link(String p_file) { return false; }; + virtual String read_link(String p_file) { return p_file; }; + virtual Error create_link(String p_source, String p_target) { return FAILED; }; + + //virtual FileType get_file_type() const; uint64_t get_space_left(); virtual String get_filesystem_type() const; diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h index e8a9d96a85f..28608153cdb 100644 --- a/platform/android/dir_access_jandroid.h +++ b/platform/android/dir_access_jandroid.h @@ -74,6 +74,10 @@ public: virtual Error rename(String p_from, String p_to); virtual Error remove(String p_name); + virtual bool is_link(String p_file) { return false; } + virtual String read_link(String p_file) { return p_file; } + virtual Error create_link(String p_source, String p_target) { return FAILED; } + virtual String get_filesystem_type() const; uint64_t get_space_left();