From c09731c4137a28f2d3ba954a11e83fdc7b535ba8 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Wed, 9 Jan 2019 10:24:23 +0100 Subject: [PATCH] Warn on filesystem case mismatch When a file is opened with a wrong case, it can work on the developer system but break on a user system with a case-sensitive filesystem. This will display a warning when it happens. CAVEATS: It will also display the warning if a symlink is in the path. Adapt warning if the file is a symlink. Avoid warning on symlinks. Fix memory leak and avoid `lstat` usage. Avoid exposing real_path when not in TOOLS_ENABLED mode. --- drivers/unix/file_access_unix.cpp | 41 +++++++++++++++++++++++++ drivers/unix/file_access_unix.h | 4 +++ drivers/windows/file_access_windows.cpp | 2 +- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index 3d584341ed9..43ad0799bab 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -41,6 +41,11 @@ #include #include +#if defined(TOOLS_ENABLED) +#include +#include +#endif + void FileAccessUnix::check_errors() const { ERR_FAIL_NULL_MSG(f, "File must be opened before use."); @@ -87,6 +92,22 @@ Error FileAccessUnix::open_internal(const String &p_path, int p_mode_flags) { } } +#if defined(TOOLS_ENABLED) + if (p_mode_flags & READ) { + String real_path = get_real_path(); + if (real_path != path) { + // Don't warn on symlinks, since they can be used to simply share addons on multiple projects. + if (real_path.to_lower() == path.to_lower()) { + // The File system is case insensitive, but other platforms can be sensitive to it + // To ease cross-platform development, we issue a warning if users try to access + // a file using the wrong case (which *works* on Windows and macOS, but won't on other + // platforms). + WARN_PRINT(vformat("Case mismatch opening requested file '%s', stored as '%s' in the filesystem. This file will not open when exported to other case-sensitive platforms.", path, real_path)); + } + } + } +#endif + if (is_backup_save_enabled() && (p_mode_flags == WRITE)) { save_path = path; // Create a temporary file in the same directory as the target file. @@ -173,6 +194,26 @@ String FileAccessUnix::get_path_absolute() const { return path; } +#if defined(TOOLS_ENABLED) +String FileAccessUnix::get_real_path() const { + char *resolved_path = ::realpath(path.utf8().get_data(), nullptr); + + if (!resolved_path) { + return path; + } + + String result; + Error parse_ok = result.parse_utf8(resolved_path); + ::free(resolved_path); + + if (parse_ok != OK) { + return path; + } + + return result.simplify_path(); +} +#endif + void FileAccessUnix::seek(uint64_t p_position) { ERR_FAIL_NULL_MSG(f, "File must be opened before use."); diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h index 76f629f7c21..7caf8a14d7b 100644 --- a/drivers/unix/file_access_unix.h +++ b/drivers/unix/file_access_unix.h @@ -51,6 +51,10 @@ class FileAccessUnix : public FileAccess { void _close(); +#if defined(TOOLS_ENABLED) + String get_real_path() const; // Returns the resolved real path for the current open file. +#endif + public: static CloseNotificationFunc close_notification_func; diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 5c06295f148..7d2247d41ac 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -127,7 +127,7 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) { } #ifdef TOOLS_ENABLED - // Windows is case insensitive, but all other platforms are sensitive to it + // Windows is case insensitive in the default configuration, but other platforms can be sensitive to it // To ease cross-platform development, we issue a warning if users try to access // a file using the wrong case (which *works* on Windows, but won't on other // platforms), we only check for relative paths, or paths in res:// or user://,