diff options
author | Morris Jobke <hey@morrisjobke.de> | 2015-01-09 00:38:40 +0100 |
---|---|---|
committer | Morris Jobke <hey@morrisjobke.de> | 2015-01-09 00:38:40 +0100 |
commit | cbb58b0d2f57e04c71819d1ff2e80bc018371aad (patch) | |
tree | bfa8d6bcd8e7b1bad24c94ff99b0ae8bcc0a0daa | |
parent | df5b5e4cf8c1ed039d45a1dacd2e0302e2f7ecce (diff) | |
parent | 3adb47094e57a81ad0af48210ea5a5e6d08b96d3 (diff) | |
download | nextcloud-server-cbb58b0d2f57e04c71819d1ff2e80bc018371aad.tar.gz nextcloud-server-cbb58b0d2f57e04c71819d1ff2e80bc018371aad.zip |
Merge pull request #12736 from owncloud/temp-handling-stable6
[stable6] Cleanup handling of temporary files
-rw-r--r-- | cron.php | 3 | ||||
-rw-r--r-- | lib/base.php | 3 | ||||
-rw-r--r-- | lib/private/helper.php | 91 | ||||
-rw-r--r-- | lib/private/server.php | 12 | ||||
-rw-r--r-- | lib/private/tempmanager.php | 145 | ||||
-rw-r--r-- | lib/public/iservercontainer.php | 7 | ||||
-rw-r--r-- | lib/public/itempmanager.php | 38 | ||||
-rw-r--r-- | tests/lib/tempmanager.php | 143 |
8 files changed, 352 insertions, 90 deletions
@@ -60,8 +60,7 @@ try { // Handle unexpected errors register_shutdown_function('handleUnexpectedShutdown'); - // Delete temp folder - OC_Helper::cleanTmpNoClean(); + \OC::$server->getTempManager()->cleanOld(); // Exit if background jobs are disabled! $appmode = OC_BackgroundJob::getExecutionType(); diff --git a/lib/base.php b/lib/base.php index 5d7408e65da..701cb22afe5 100644 --- a/lib/base.php +++ b/lib/base.php @@ -585,7 +585,8 @@ class OC { self::registerLogRotate(); //make sure temporary files are cleaned up - register_shutdown_function(array('OC_Helper', 'cleanTmp')); + $tmpManager = \OC::$server->getTempManager(); + register_shutdown_function(array($tmpManager, 'clean')); //parse the given parameters self::$REQUESTEDAPP = (isset($_GET['app']) && trim($_GET['app']) != '' && !is_null($_GET['app']) ? OC_App::cleanAppId(strip_tags($_GET['app'])) : OC_Config::getValue('defaultapp', 'files')); diff --git a/lib/private/helper.php b/lib/private/helper.php index cca9d29b4b1..318aaffc067 100644 --- a/lib/private/helper.php +++ b/lib/private/helper.php @@ -25,7 +25,6 @@ * Collection of useful functions */ class OC_Helper { - private static $tmpFiles = array(); private static $mimetypeIcons = array(); private static $mimetypeDetector; private static $templateManager; @@ -548,106 +547,24 @@ class OC_Helper { * * @param string $postfix * @return string + * @deprecated Use the TempManager instead * * temporary files are automatically cleaned up after the script is finished */ public static function tmpFile($postfix = '') { - $file = get_temp_dir() . '/' . md5(time() . rand()) . $postfix; - $fh = fopen($file, 'w'); - fclose($fh); - self::$tmpFiles[] = $file; - return $file; - } - - /** - * move a file to oc-noclean temp dir - * - * @param string $filename - * @return mixed - * - */ - public static function moveToNoClean($filename = '') { - if ($filename == '') { - return false; - } - $tmpDirNoClean = get_temp_dir() . '/oc-noclean/'; - if (!file_exists($tmpDirNoClean) || !is_dir($tmpDirNoClean)) { - if (file_exists($tmpDirNoClean)) { - unlink($tmpDirNoClean); - } - mkdir($tmpDirNoClean); - } - $newname = $tmpDirNoClean . basename($filename); - if (rename($filename, $newname)) { - return $newname; - } else { - return false; - } + return \OC::$server->getTempManager()->getTemporaryFile($postfix); } /** * create a temporary folder with an unique filename * * @return string + * @deprecated Use the TempManager instead * * temporary files are automatically cleaned up after the script is finished */ public static function tmpFolder() { - $path = get_temp_dir() . '/' . md5(time() . rand()); - mkdir($path); - self::$tmpFiles[] = $path; - return $path . '/'; - } - - /** - * remove all files created by self::tmpFile - */ - public static function cleanTmp() { - $leftoversFile = get_temp_dir() . '/oc-not-deleted'; - if (file_exists($leftoversFile)) { - $leftovers = file($leftoversFile); - foreach ($leftovers as $file) { - self::rmdirr($file); - } - unlink($leftoversFile); - } - - foreach (self::$tmpFiles as $file) { - if (file_exists($file)) { - if (!self::rmdirr($file)) { - file_put_contents($leftoversFile, $file . "\n", FILE_APPEND); - } - } - } - } - - /** - * remove all files in PHP /oc-noclean temp dir - */ - public static function cleanTmpNoClean() { - $tmpDirNoCleanName=get_temp_dir() . '/oc-noclean/'; - if(file_exists($tmpDirNoCleanName) && is_dir($tmpDirNoCleanName)) { - $files=scandir($tmpDirNoCleanName); - foreach($files as $file) { - $fileName = $tmpDirNoCleanName . $file; - if (!\OC\Files\Filesystem::isIgnoredDir($file) && filemtime($fileName) + 600 < time()) { - unlink($fileName); - } - } - // if oc-noclean is empty delete it - $isTmpDirNoCleanEmpty = true; - $tmpDirNoClean = opendir($tmpDirNoCleanName); - if(is_resource($tmpDirNoClean)) { - while (false !== ($file = readdir($tmpDirNoClean))) { - if (!\OC\Files\Filesystem::isIgnoredDir($file)) { - $isTmpDirNoCleanEmpty = false; - } - } - } - if ($isTmpDirNoCleanEmpty) { - rmdir($tmpDirNoCleanName); - } - } + return \OC::$server->getTempManager()->getTemporaryFolder(); } /** diff --git a/lib/private/server.php b/lib/private/server.php index 0e8af4b797e..f1c823e0dd0 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -153,6 +153,10 @@ class Server extends SimpleContainer implements IServerContainer { $config = $c->query('AllConfig'); return new HTTPHelper($config); }); + $this->registerService('TempManager', function ($c) { + /** @var Server $c */ + return new TempManager(get_temp_dir(), \OC_Log::$object); + }); } /** @@ -356,4 +360,12 @@ class Server extends SimpleContainer implements IServerContainer { return $this->query('HTTPHelper'); } + /** + * Get the manager for temporary files and folders + * + * @return \OCP\ITempManager + */ + function getTempManager() { + return $this->query('TempManager'); + } } diff --git a/lib/private/tempmanager.php b/lib/private/tempmanager.php new file mode 100644 index 00000000000..4b9ac6b6b1e --- /dev/null +++ b/lib/private/tempmanager.php @@ -0,0 +1,145 @@ +<?php + +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC; + +use OCP\ITempManager; + +class TempManager implements ITempManager { + /** + * Current temporary files and folders + * + * @var string[] + */ + protected $current = array(); + + /** + * i.e. /tmp on linux systems + * + * @var string + */ + protected $tmpBaseDir; + + /** + * @var \OC\Log + */ + protected $log; + + /** + * @param string $baseDir + * @param \OC\Log $logger + */ + public function __construct($baseDir, Log $logger) { + $this->tmpBaseDir = $baseDir; + $this->log = $logger; + } + + protected function generatePath($postFix) { + return $this->tmpBaseDir . '/oc_tmp_' . md5(time() . rand()) . $postFix; + } + + /** + * Create a temporary file and return the path + * + * @param string $postFix + * @return string + */ + public function getTemporaryFile($postFix = '') { + $file = $this->generatePath($postFix); + if (is_writable($this->tmpBaseDir)) { + touch($file); + $this->current[] = $file; + return $file; + } else { + $this->log->warning( + 'Can not create a temporary file in directory {dir}. Check it exists and has correct permissions', + array( + 'dir' => $this->tmpBaseDir + ) + ); + return false; + } + } + + /** + * Create a temporary folder and return the path + * + * @param string $postFix + * @return string + */ + public function getTemporaryFolder($postFix = '') { + $path = $this->generatePath($postFix); + if (is_writable($this->tmpBaseDir)) { + mkdir($path); + $this->current[] = $path; + return $path . '/'; + } else { + $this->log->warning( + 'Can not create a temporary folder in directory {dir}. Check it exists and has correct permissions', + array( + 'dir' => $this->tmpBaseDir + ) + ); + return false; + } + } + + /** + * Remove the temporary files and folders generated during this request + */ + public function clean() { + $this->cleanFiles($this->current); + } + + protected function cleanFiles($files) { + foreach ($files as $file) { + if (file_exists($file)) { + try { + \OC_Helper::rmdirr($file); + } catch (\UnexpectedValueException $ex) { + $this->log->warning( + "Error deleting temporary file/folder: {file} - Reason: {error}", + array( + 'file' => $file, + 'error' => $ex->getMessage() + ) + ); + } + } + } + } + + /** + * Remove old temporary files and folders that were failed to be cleaned + */ + public function cleanOld() { + $this->cleanFiles($this->getOldFiles()); + } + + /** + * Get all temporary files and folders generated by oc older than an hour + * + * @return string[] + */ + protected function getOldFiles() { + $cutOfTime = time() - 3600; + $files = array(); + $dh = opendir($this->tmpBaseDir); + while (($file = readdir($dh)) !== false) { + if (substr($file, 0, 7) === 'oc_tmp_') { + $path = $this->tmpBaseDir . '/' . $file; + $mtime = filemtime($path); + if ($mtime < $cutOfTime) { + $files[] = $path; + } + } + } + return $files; + } +} diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index dc59e01bced..f44e5fba26c 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -174,4 +174,11 @@ interface IServerContainer { * @return \OC\HTTPHelper */ function getHTTPHelper(); + + /** + * Get the manager for temporary files and folders + * + * @return \OCP\ITempManager + */ + function getTempManager(); } diff --git a/lib/public/itempmanager.php b/lib/public/itempmanager.php new file mode 100644 index 00000000000..ebd94978038 --- /dev/null +++ b/lib/public/itempmanager.php @@ -0,0 +1,38 @@ +<?php + +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCP; + +interface ITempManager { + /** + * Create a temporary file and return the path + * + * @param string $postFix + * @return string + */ + public function getTemporaryFile($postFix = ''); + + /** + * Create a temporary folder and return the path + * + * @param string $postFix + * @return string + */ + public function getTemporaryFolder($postFix = ''); + + /** + * Remove the temporary files and folders generated during this request + */ + public function clean(); + + /** + * Remove old temporary files and folders that were failed to be cleaned + */ + public function cleanOld(); +} diff --git a/tests/lib/tempmanager.php b/tests/lib/tempmanager.php new file mode 100644 index 00000000000..f16fbce2c7c --- /dev/null +++ b/tests/lib/tempmanager.php @@ -0,0 +1,143 @@ +<?php + +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test; + +use OC\Log; + +class NullLogger extends Log { + public function __construct($logger = null) { + //disable original constructor + } + + public function log($level, $message, array $context = array()) { + //noop + } +} + +class TempManager extends \PHPUnit_Framework_TestCase { + protected $baseDir; + + public function setUp() { + $this->baseDir = get_temp_dir() . '/oc_tmp_test'; + if (!is_dir($this->baseDir)) { + mkdir($this->baseDir); + } + } + + public function tearDown() { + \OC_Helper::rmdirr($this->baseDir); + } + + /** + * @param \Psr\Log\LoggerInterface $logger + * @return \OC\TempManager + */ + protected function getManager($logger = null) { + if (!$logger) { + $logger = new NullLogger(); + } + return new \OC\TempManager($this->baseDir, $logger); + } + + public function testGetFile() { + $manager = $this->getManager(); + $file = $manager->getTemporaryFile('.txt'); + $this->assertStringEndsWith('.txt', $file); + $this->assertTrue(is_file($file)); + $this->assertTrue(is_writable($file)); + + file_put_contents($file, 'bar'); + $this->assertEquals('bar', file_get_contents($file)); + } + + public function testGetFolder() { + $manager = $this->getManager(); + $folder = $manager->getTemporaryFolder(); + $this->assertStringEndsWith('/', $folder); + $this->assertTrue(is_dir($folder)); + $this->assertTrue(is_writable($folder)); + + file_put_contents($folder . 'foo.txt', 'bar'); + $this->assertEquals('bar', file_get_contents($folder . 'foo.txt')); + } + + public function testCleanFiles() { + $manager = $this->getManager(); + $file1 = $manager->getTemporaryFile('.txt'); + $file2 = $manager->getTemporaryFile('.txt'); + $this->assertTrue(file_exists($file1)); + $this->assertTrue(file_exists($file2)); + + $manager->clean(); + + $this->assertFalse(file_exists($file1)); + $this->assertFalse(file_exists($file2)); + } + + public function testCleanFolder() { + $manager = $this->getManager(); + $folder1 = $manager->getTemporaryFolder(); + $folder2 = $manager->getTemporaryFolder(); + touch($folder1 . 'foo.txt'); + touch($folder1 . 'bar.txt'); + $this->assertTrue(file_exists($folder1)); + $this->assertTrue(file_exists($folder2)); + $this->assertTrue(file_exists($folder1 . 'foo.txt')); + $this->assertTrue(file_exists($folder1 . 'bar.txt')); + + $manager->clean(); + + $this->assertFalse(file_exists($folder1)); + $this->assertFalse(file_exists($folder2)); + $this->assertFalse(file_exists($folder1 . 'foo.txt')); + $this->assertFalse(file_exists($folder1 . 'bar.txt')); + } + + public function testCleanOld() { + $manager = $this->getManager(); + $oldFile = $manager->getTemporaryFile('.txt'); + $newFile = $manager->getTemporaryFile('.txt'); + $folder = $manager->getTemporaryFolder(); + $nonOcFile = $this->baseDir . '/foo.txt'; + file_put_contents($nonOcFile, 'bar'); + + $past = time() - 2 * 3600; + touch($oldFile, $past); + touch($folder, $past); + touch($nonOcFile, $past); + + $manager2 = $this->getManager(); + $manager2->cleanOld(); + $this->assertFalse(file_exists($oldFile)); + $this->assertFalse(file_exists($folder)); + $this->assertTrue(file_exists($nonOcFile)); + $this->assertTrue(file_exists($newFile)); + } + + public function testLogCantCreateFile() { + $logger = $this->getMock('\Test\NullLogger'); + $manager = $this->getManager($logger); + chmod($this->baseDir, 0500); + $logger->expects($this->once()) + ->method('warning') + ->with($this->stringContains('Can not create a temporary file in directory')); + $this->assertFalse($manager->getTemporaryFile('.txt')); + } + + public function testLogCantCreateFolder() { + $logger = $this->getMock('\Test\NullLogger'); + $manager = $this->getManager($logger); + chmod($this->baseDir, 0500); + $logger->expects($this->once()) + ->method('warning') + ->with($this->stringContains('Can not create a temporary folder in directory')); + $this->assertFalse($manager->getTemporaryFolder()); + } +} |