summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorris Jobke <hey@morrisjobke.de>2015-01-09 00:38:40 +0100
committerMorris Jobke <hey@morrisjobke.de>2015-01-09 00:38:40 +0100
commitcbb58b0d2f57e04c71819d1ff2e80bc018371aad (patch)
treebfa8d6bcd8e7b1bad24c94ff99b0ae8bcc0a0daa
parentdf5b5e4cf8c1ed039d45a1dacd2e0302e2f7ecce (diff)
parent3adb47094e57a81ad0af48210ea5a5e6d08b96d3 (diff)
downloadnextcloud-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.php3
-rw-r--r--lib/base.php3
-rw-r--r--lib/private/helper.php91
-rw-r--r--lib/private/server.php12
-rw-r--r--lib/private/tempmanager.php145
-rw-r--r--lib/public/iservercontainer.php7
-rw-r--r--lib/public/itempmanager.php38
-rw-r--r--tests/lib/tempmanager.php143
8 files changed, 352 insertions, 90 deletions
diff --git a/cron.php b/cron.php
index 0d2c07b2d95..2d911f5515b 100644
--- a/cron.php
+++ b/cron.php
@@ -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());
+ }
+}