diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php new file mode 100644 index 0000000000..4f481cba55 --- /dev/null +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php @@ -0,0 +1,59 @@ + + * @author Robin Appelman + * @author Thomas Müller + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\DAV\Tests\unit\Connector\Sabre\RequestTest; + +use OC\Connector\Sabre\Exception\FileLocked; +use OCP\AppFramework\Http; +use OCP\Lock\ILockingProvider; + +/** + * Class DeleteTest + * + * @group DB + * + * @package OCA\DAV\Tests\unit\Connector\Sabre\RequestTest + */ +class DeleteTest extends RequestTest { + public function testBasicUpload() { + $user = $this->getUniqueID(); + $view = $this->setupUser($user, 'pass'); + + $view->file_put_contents('foo.txt', 'asd'); + $mount = $view->getMount('foo.txt'); + $internalPath = $view->getAbsolutePath(); + + // create a ghost file + $mount->getStorage()->unlink($mount->getInternalPath($internalPath)); + + // cache entry still exists + $this->assertInstanceOf('\OCP\Files\FileInfo', $view->getFileInfo('foo.txt')); + + $response = $this->request($view, $user, 'pass', 'DELETE', '/foo.txt'); + + $this->assertEquals(Http::STATUS_NO_CONTENT, $response->getStatus()); + + // no longer in the cache + $this->assertFalse($view->getFileInfo('foo.txt')); + } +} diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php index b07e26a335..acd4c3b483 100644 --- a/lib/private/Files/Storage/Local.php +++ b/lib/private/Files/Storage/Local.php @@ -157,7 +157,7 @@ class Local extends \OC\Files\Storage\Common { public function filemtime($path) { clearstatcache($this->getSourcePath($path)); - return filemtime($this->getSourcePath($path)); + return $this->file_exists($path) ? filemtime($this->getSourcePath($path)) : false; } public function touch($path, $mtime = null) { @@ -188,7 +188,7 @@ class Local extends \OC\Files\Storage\Common { return ''; } - $handle = fopen($fileName,'rb'); + $handle = fopen($fileName, 'rb'); $content = fread($handle, $fileSize); fclose($handle); return $content; @@ -377,7 +377,7 @@ class Local extends \OC\Files\Storage\Common { * @return bool */ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { - if($sourceStorage->instanceOfStorage('\OC\Files\Storage\Local')){ + if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Local')) { /** * @var \OC\Files\Storage\Local $sourceStorage */ diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php index f738542ea8..e9daa12347 100644 --- a/lib/private/Files/View.php +++ b/lib/private/Files/View.php @@ -337,10 +337,17 @@ class View { return $this->removeMount($mount, $absolutePath); } if ($this->is_dir($path)) { - return $this->basicOperation('rmdir', $path, array('delete')); + $result = $this->basicOperation('rmdir', $path, array('delete')); } else { - return false; + $result = false; } + + if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete + $storage = $mount->getStorage(); + $internalPath = $mount->getInternalPath($absolutePath); + $storage->getUpdater()->remove($internalPath); + } + return $result; } /** @@ -429,7 +436,7 @@ class View { /** * @param string $path - * @param int $from + * @param int $from * @param int $to * @return bool|mixed * @throws \OCP\Files\InvalidPathException @@ -441,18 +448,18 @@ class View { $handle = $this->fopen($path, 'rb'); if ($handle) { if (fseek($handle, $from) === 0) { - $chunkSize = 8192; // 8 kB chunks - $end = $to + 1; - while (!feof($handle) && ftell($handle) < $end) { - $len = $end-ftell($handle); - if ($len > $chunkSize) { - $len = $chunkSize; + $chunkSize = 8192; // 8 kB chunks + $end = $to + 1; + while (!feof($handle) && ftell($handle) < $end) { + $len = $end - ftell($handle); + if ($len > $chunkSize) { + $len = $chunkSize; + } + echo fread($handle, $len); + flush(); } - echo fread($handle, $len); - flush(); - } - $size = ftell($handle) - $from; - return $size; + $size = ftell($handle) - $from; + return $size; } throw new \OCP\Files\UnseekableException('fseek error'); @@ -679,7 +686,15 @@ class View { if ($mount and $mount->getInternalPath($absolutePath) === '') { return $this->removeMount($mount, $absolutePath); } - return $this->basicOperation('unlink', $path, array('delete')); + $result = $this->basicOperation('unlink', $path, array('delete')); + if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete + $storage = $mount->getStorage(); + $internalPath = $mount->getInternalPath($absolutePath); + $storage->getUpdater()->remove($internalPath); + return true; + } else { + return $result; + } } /** diff --git a/tests/lib/Files/ViewTest.php b/tests/lib/Files/ViewTest.php index 2c27bb64a7..59b17b8395 100644 --- a/tests/lib/Files/ViewTest.php +++ b/tests/lib/Files/ViewTest.php @@ -2417,7 +2417,7 @@ class ViewTest extends \Test\TestCase { $content = $view->getDirectoryContent('', $filter); - $files = array_map(function(FileInfo $info) { + $files = array_map(function (FileInfo $info) { return $info->getName(); }, $content); sort($files); @@ -2444,4 +2444,53 @@ class ViewTest extends \Test\TestCase { $data = $view->getFileInfo('.'); $this->assertEquals('', $data->getChecksum()); } + + public function testDeleteGhostFile() { + $storage = new Temporary(array()); + $scanner = $storage->getScanner(); + $cache = $storage->getCache(); + $storage->file_put_contents('foo.txt', 'bar'); + \OC\Files\Filesystem::mount($storage, array(), '/test/'); + $scanner->scan(''); + + $storage->unlink('foo.txt'); + + $this->assertTrue($cache->inCache('foo.txt')); + + $view = new \OC\Files\View('/test'); + $rootInfo = $view->getFileInfo(''); + $this->assertEquals(3, $rootInfo->getSize()); + $view->unlink('foo.txt'); + $newInfo = $view->getFileInfo(''); + + $this->assertFalse($cache->inCache('foo.txt')); + $this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag()); + $this->assertEquals(0, $newInfo->getSize()); + } + + public function testDeleteGhostFolder() { + $storage = new Temporary(array()); + $scanner = $storage->getScanner(); + $cache = $storage->getCache(); + $storage->mkdir('foo'); + $storage->file_put_contents('foo/foo.txt', 'bar'); + \OC\Files\Filesystem::mount($storage, array(), '/test/'); + $scanner->scan(''); + + $storage->rmdir('foo'); + + $this->assertTrue($cache->inCache('foo')); + $this->assertTrue($cache->inCache('foo/foo.txt')); + + $view = new \OC\Files\View('/test'); + $rootInfo = $view->getFileInfo(''); + $this->assertEquals(3, $rootInfo->getSize()); + $view->rmdir('foo'); + $newInfo = $view->getFileInfo(''); + + $this->assertFalse($cache->inCache('foo')); + $this->assertFalse($cache->inCache('foo/foo.txt')); + $this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag()); + $this->assertEquals(0, $newInfo->getSize()); + } }