From d4af6de7fc2795a18dabceb78106c90aae982946 Mon Sep 17 00:00:00 2001 From: will wade Date: Thu, 18 Jun 2026 14:41:25 +0100 Subject: [PATCH] IsFileWriteable: probe via permission bits, not append-open The previous implementation tested writability by opening the file with ios_base::app|out, which: 1. As a side effect, created empty files when probing non-existent paths (relevant on platforms where ScanFiles might pass through transient candidate paths). 2. On iOS keyboard extensions, where the data bundle is read-only and sandboxed, generated a 'file-write-data' sandbox deny for every bundled file scanned (colour XMLs, alphabet XMLs, etc.). Each deny produced a kernel log line and forced the kernel to set up + tear down a denied file descriptor. Replace the open-with-append with std::filesystem::status(), checking the permission bits for any write bit (owner/group/others). This is: - Side-effect free: never creates files. - Faster: one stat() per file instead of open()+close(). - Sandbox-clean: no write attempts against read-only paths. Semantically equivalent for all callers (ScanFiles only invokes IsFileWriteable on paths that already exist as regular files, so the 'non-existent file' case doesn't apply in practice). Signed-off-by: will wade --- src/DasherCore/FileUtils.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/DasherCore/FileUtils.cpp b/src/DasherCore/FileUtils.cpp index 3b3bbc3b..0d17b036 100644 --- a/src/DasherCore/FileUtils.cpp +++ b/src/DasherCore/FileUtils.cpp @@ -17,8 +17,16 @@ void FileUtils::SetDataDirectory(const std::string& dataDir) { } // namespace Dasher static bool IsFileWriteable(const std::filesystem::path& file_path) { - std::fstream file(file_path.string(), std::ios_base::app | std::fstream::out); - return file.is_open(); + // Check writability via permission bits without opening the file. + // Opening with ios_base::app|out (the previous implementation) attempted + // to create/append the file just to test writability, which generated + // sandbox deny storms against read-only bundled data on iOS keyboard + // extensions and could accidentally create empty files on other platforms. + std::error_code ec; + const auto status = std::filesystem::status(file_path, ec); + if (ec) return false; + using P = std::filesystem::perms; + return (status.permissions() & (P::owner_write | P::group_write | P::others_write)) != P::none; } int Dasher::FileUtils::GetFileSize(const std::string& strFileName) {