Merge pull request #11709 from owncloud/temp-handling
Cleanup handling of temporary filesremotes/origin/fix-10825
commit
9fa5483111
@ -0,0 +1,146 @@ |
||||
<?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\ILogger; |
||||
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 \OCP\ILogger |
||||
*/ |
||||
protected $log; |
||||
|
||||
/** |
||||
* @param string $baseDir |
||||
* @param \OCP\ILogger $logger |
||||
*/ |
||||
public function __construct($baseDir, ILogger $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; |
||||
} |
||||
} |
||||
@ -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(); |
||||
} |
||||
@ -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()); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue