Check if main directories are writable during Core updates
authorMatthias Schmidt <gravatronics@live.com>
Sun, 18 Aug 2019 10:40:35 +0000 (12:40 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Sun, 18 Aug 2019 10:40:35 +0000 (12:40 +0200)
See #2847

wcfsetup/install/files/lib/system/WCF.class.php
wcfsetup/install/files/lib/system/package/PackageInstallationDispatcher.class.php

index 4faf6054dc2f4e8eaf01c4c69db4a5c186f6adf5..8248040996c764471f64bc7107d85cd559eb7b2c 100644 (file)
@@ -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 : ''));
+               }
+       }
 }
index 8ea75049bba00925f65202b4f6e4ba163b8d1aa3..551219baae6fa049658e2ee6acc14675f09577c9 100644 (file)
@@ -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;