From 66d9b64b2f327cefc3d0659e64daee01e8ed4620 Mon Sep 17 00:00:00 2001 From: Matthias Schmidt Date: Sun, 18 Aug 2019 12:40:35 +0200 Subject: [PATCH] Check if main directories are writable during Core updates See #2847 --- .../install/files/lib/system/WCF.class.php | 74 +++++++++++++++++++ .../PackageInstallationDispatcher.class.php | 5 +- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/wcfsetup/install/files/lib/system/WCF.class.php b/wcfsetup/install/files/lib/system/WCF.class.php index 4faf6054dc..8248040996 100644 --- a/wcfsetup/install/files/lib/system/WCF.class.php +++ b/wcfsetup/install/files/lib/system/WCF.class.php @@ -34,6 +34,7 @@ use wcf\system\style\StyleHandler; use wcf\system\template\EmailTemplateEngine; use wcf\system\template\TemplateEngine; use wcf\system\user\storage\UserStorageHandler; +use wcf\util\DirectoryUtil; use wcf\util\FileUtil; use wcf\util\HeaderUtil; use wcf\util\StringUtil; @@ -1129,4 +1130,77 @@ class WCF { self::getTPL()->assign('executeCronjobs', CronjobScheduler::getInstance()->getNextExec() < TIME_NOW && defined('OFFLINE') && !OFFLINE); } } + + /** + * Checks recursively that the most important system files of `com.woltlab.wcf` are writable. + * + * @throws \RuntimeException if any relevant file or directory is not writable + */ + public static function checkWritability() { + $nonWritablePaths = []; + + $nonRecursiveDirectories = [ + '', + 'acp/', + 'tmp/' + ]; + foreach ($nonRecursiveDirectories as $directory) { + $path = WCF_DIR . $directory; + if ($path === 'tmp/' && !is_dir($path)) { + continue; + } + + if (!is_writable($path)) { + $nonWritablePaths[] = $path; + continue; + } + + DirectoryUtil::getInstance($path, false)->executeCallback(function($file, \SplFileInfo $fileInfo) use ($path, &$nonWritablePaths) { + if ($fileInfo instanceof \DirectoryIterator) { + if (!is_writable($fileInfo->getPath())) { + $nonWritablePaths[] = $fileInfo->getPath(); + } + } + else if (!is_writable($fileInfo->getRealPath())) { + $nonWritablePaths[] = $fileInfo->getRealPath(); + } + }); + } + + $recursiveDirectories = [ + 'acp/js/', + 'acp/style/', + 'acp/templates/', + 'acp/uninstall/', + 'js/', + 'lib/', + 'log/', + 'style/', + 'templates/' + ]; + foreach ($recursiveDirectories as $directory) { + $path = WCF_DIR . $directory; + + if (!is_writable($path)) { + $nonWritablePaths[] = $path; + continue; + } + + DirectoryUtil::getInstance($path)->executeCallback(function($file, \SplFileInfo $fileInfo) use ($path, &$nonWritablePaths) { + if ($fileInfo instanceof \DirectoryIterator) { + if (!is_writable($fileInfo->getPath())) { + $nonWritablePaths[] = $fileInfo->getPath(); + } + } + else if (!is_writable($fileInfo->getRealPath())) { + $nonWritablePaths[] = $fileInfo->getRealPath(); + } + }); + } + + if (!empty($nonWritablePaths)) { + $maxPaths = 10; + throw new \RuntimeException('The following paths are not writable: ' . implode(',' ,array_slice($nonWritablePaths, 0, $maxPaths)) . (count($nonWritablePaths) > $maxPaths ? ',' . StringUtil::HELLIP : '')); + } + } } diff --git a/wcfsetup/install/files/lib/system/package/PackageInstallationDispatcher.class.php b/wcfsetup/install/files/lib/system/package/PackageInstallationDispatcher.class.php index 8ea75049bb..551219baae 100644 --- a/wcfsetup/install/files/lib/system/package/PackageInstallationDispatcher.class.php +++ b/wcfsetup/install/files/lib/system/package/PackageInstallationDispatcher.class.php @@ -396,6 +396,10 @@ class PackageInstallationDispatcher { protected function installPackage(array $nodeData) { $installationStep = new PackageInstallationStep(); + if ($this->getAction() === 'update' && $this->getPackageName() === 'com.woltlab.wcf') { + WCF::checkWritability(); + } + // check requirements if (!empty($nodeData['requirements'])) { foreach ($nodeData['requirements'] as $package => $requirementData) { @@ -1176,7 +1180,6 @@ class PackageInstallationDispatcher { ]; } } - } return $errors; -- 2.20.1