From 25370fcb8235d2129cab0f8a5843c4784b3673d0 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 2 Jan 2014 13:19:10 +0100 Subject: Return SPACE_UNKNOWN if disk_free_space is disabled when getting the free space on a local storage --- lib/private/files/storage/local.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/private/files') diff --git a/lib/private/files/storage/local.php b/lib/private/files/storage/local.php index 02e8df4af4e..db3c6bfca3a 100644 --- a/lib/private/files/storage/local.php +++ b/lib/private/files/storage/local.php @@ -256,7 +256,7 @@ if (\OC_Util::runningOnWindows()) { public function free_space($path) { $space = @disk_free_space($this->datadir . $path); - if ($space === false) { + if ($space === false || is_null($space)) { return \OC\Files\SPACE_UNKNOWN; } return $space; -- cgit v1.2.3 From 5be4af9f519ef8d0424f8b07ba84c43da9cf11f4 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 7 Jan 2014 17:41:04 +0100 Subject: Now also preventing to override "files" dir size with -1 Fixes #6526 --- lib/private/files/cache/homecache.php | 2 +- tests/lib/files/cache/homecache.php | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'lib/private/files') diff --git a/lib/private/files/cache/homecache.php b/lib/private/files/cache/homecache.php index 18dfbfe3191..71bb944da71 100644 --- a/lib/private/files/cache/homecache.php +++ b/lib/private/files/cache/homecache.php @@ -16,7 +16,7 @@ class HomeCache extends Cache { * @return int */ public function calculateFolderSize($path) { - if ($path !== '/' and $path !== '') { + if ($path !== '/' and $path !== '' and $path !== 'files') { return parent::calculateFolderSize($path); } diff --git a/tests/lib/files/cache/homecache.php b/tests/lib/files/cache/homecache.php index 2fa7f1ba92e..87fd0dba4c6 100644 --- a/tests/lib/files/cache/homecache.php +++ b/tests/lib/files/cache/homecache.php @@ -62,33 +62,39 @@ class HomeCache extends \PHPUnit_Framework_TestCase { } /** - * Tests that the root folder size calculation ignores the subdirs that have an unknown - * size. This makes sure that quota calculation still works as it's based on the root - * folder size. + * Tests that the root and files folder size calculation ignores the subdirs + * that have an unknown size. This makes sure that quota calculation still + * works as it's based on the "files" folder size. */ public function testRootFolderSizeIgnoresUnknownUpdate() { - $dir1 = 'knownsize'; - $dir2 = 'unknownsize'; + $dir1 = 'files/knownsize'; + $dir2 = 'files/unknownsize'; $fileData = array(); $fileData[''] = array('size' => -1, 'mtime' => 20, 'mimetype' => 'httpd/unix-directory'); + $fileData['files'] = array('size' => -1, 'mtime' => 20, 'mimetype' => 'httpd/unix-directory'); $fileData[$dir1] = array('size' => 1000, 'mtime' => 20, 'mimetype' => 'httpd/unix-directory'); $fileData[$dir2] = array('size' => -1, 'mtime' => 25, 'mimetype' => 'httpd/unix-directory'); $this->cache->put('', $fileData['']); + $this->cache->put('files', $fileData['files']); $this->cache->put($dir1, $fileData[$dir1]); $this->cache->put($dir2, $fileData[$dir2]); + $this->assertTrue($this->cache->inCache('files')); $this->assertTrue($this->cache->inCache($dir1)); $this->assertTrue($this->cache->inCache($dir2)); - // check that root size ignored the unknown sizes + // check that files and root size ignored the unknown sizes + $this->assertEquals(1000, $this->cache->calculateFolderSize('files')); $this->assertEquals(1000, $this->cache->calculateFolderSize('')); // clean up $this->cache->remove(''); + $this->cache->remove('files'); $this->cache->remove($dir1); $this->cache->remove($dir2); + $this->assertFalse($this->cache->inCache('files')); $this->assertFalse($this->cache->inCache($dir1)); $this->assertFalse($this->cache->inCache($dir2)); } -- cgit v1.2.3 From f642ad39614970de67358d80517bc1d76a006e0e Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 8 Jan 2014 13:17:36 +0100 Subject: Prevent deleting storage root Storage mount points are not deletable, so make sure that the unlink operation and its hooks aren't run in such cases. Note that some storages might recursively delete their contents when calling unlink on their root. This fix prevents that as well. --- lib/private/files/view.php | 13 +++++++++++++ tests/lib/files/view.php | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) (limited to 'lib/private/files') diff --git a/lib/private/files/view.php b/lib/private/files/view.php index ac45a881331..8893911ed5d 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -336,6 +336,19 @@ class View { } public function unlink($path) { + if ($path === '' || $path === '/') { + // do not allow deleting the root + return false; + } + $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; + $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); + list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); + if (!$internalPath || $internalPath === '' || $internalPath === '/') { + // do not allow deleting the storage's root / the mount point + // because for some storages it might delete the whole contents + // but isn't supposed to work that way + return false; + } return $this->basicOperation('unlink', $path, array('delete')); } diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index b59cef9f0da..76a7fd5f1ca 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -306,6 +306,48 @@ class View extends \PHPUnit_Framework_TestCase { $this->assertTrue($rootView->file_exists('anotherfolder/bar.txt')); } + /** + * @medium + */ + function testUnlink() { + $storage1 = $this->getTestStorage(); + $storage2 = $this->getTestStorage(); + \OC\Files\Filesystem::mount($storage1, array(), '/'); + \OC\Files\Filesystem::mount($storage2, array(), '/substorage'); + + $rootView = new \OC\Files\View(''); + $rootView->file_put_contents('/foo.txt', 'asd'); + $rootView->file_put_contents('/substorage/bar.txt', 'asd'); + + $this->assertTrue($rootView->file_exists('foo.txt')); + $this->assertTrue($rootView->file_exists('substorage/bar.txt')); + + $this->assertTrue($rootView->unlink('foo.txt')); + $this->assertTrue($rootView->unlink('substorage/bar.txt')); + + $this->assertFalse($rootView->file_exists('foo.txt')); + $this->assertFalse($rootView->file_exists('substorage/bar.txt')); + } + + /** + * @medium + */ + function testUnlinkRootMustFail() { + $storage1 = $this->getTestStorage(); + $storage2 = $this->getTestStorage(); + \OC\Files\Filesystem::mount($storage1, array(), '/'); + \OC\Files\Filesystem::mount($storage2, array(), '/substorage'); + + $rootView = new \OC\Files\View(''); + $rootView->file_put_contents('/foo.txt', 'asd'); + $rootView->file_put_contents('/substorage/bar.txt', 'asd'); + + $this->assertFalse($rootView->unlink('')); + $this->assertFalse($rootView->unlink('/')); + $this->assertFalse($rootView->unlink('substorage')); + $this->assertFalse($rootView->unlink('/substorage')); + } + /** * @medium */ -- cgit v1.2.3 From 4faba49f0a38427e96ef8393900f799c5a5ba6aa Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 9 Jan 2014 17:27:55 +0100 Subject: Fix calculated folder size to use unencrypted size The encrypted size was used when calculating folder sizes. This fix now also sums up the unencrypted size and shows that one when available. --- lib/private/files/cache/cache.php | 22 ++++++++++++++----- tests/lib/files/cache/cache.php | 45 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) (limited to 'lib/private/files') diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php index 8e682a96b75..1e7936ca26d 100644 --- a/lib/private/files/cache/cache.php +++ b/lib/private/files/cache/cache.php @@ -178,7 +178,7 @@ class Cache { if ($file['storage_mtime'] == 0) { $file['storage_mtime'] = $file['mtime']; } - if ($file['encrypted']) { + if ($file['encrypted'] or ($file['unencrypted_size'] > 0 and $file['mimetype'] === 'httpd/unix-directory')) { $file['encrypted_size'] = $file['size']; $file['size'] = $file['unencrypted_size']; } @@ -511,22 +511,34 @@ class Cache { $entry = $this->get($path); if ($entry && $entry['mimetype'] === 'httpd/unix-directory') { $id = $entry['fileid']; - $sql = 'SELECT SUM(`size`) AS f1, MIN(`size`) AS f2 FROM `*PREFIX*filecache` '. + $sql = 'SELECT SUM(`size`) AS f1, MIN(`size`) AS f2, ' . + 'SUM(`unencrypted_size`) AS f3 ' . + 'FROM `*PREFIX*filecache` ' . 'WHERE `parent` = ? AND `storage` = ?'; $result = \OC_DB::executeAudited($sql, array($id, $this->getNumericStorageId())); if ($row = $result->fetchRow()) { - list($sum, $min) = array_values($row); + list($sum, $min, $unencryptedSum) = array_values($row); $sum = (int)$sum; $min = (int)$min; + $unencryptedSum = (int)$unencryptedSum; if ($min === -1) { $totalSize = $min; } else { $totalSize = $sum; } + $update = array(); if ($entry['size'] !== $totalSize) { - $this->update($id, array('size' => $totalSize)); + $update['size'] = $totalSize; + } + if ($entry['unencrypted_size'] !== $unencryptedSum) { + $update['unencrypted_size'] = $unencryptedSum; + } + if (count($update) > 0) { + $this->update($id, $update); + } + if ($totalSize !== -1 and $unencryptedSum > 0) { + $totalSize = $unencryptedSum; } - } } return $totalSize; diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 052d70dd0b4..7d9329328a3 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -137,6 +137,51 @@ class Cache extends \PHPUnit_Framework_TestCase { $this->assertFalse($this->cache->inCache('folder/bar')); } + public function testEncryptedFolder() { + $file1 = 'folder'; + $file2 = 'folder/bar'; + $file3 = 'folder/foo'; + $data1 = array('size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory'); + $fileData = array(); + $fileData['bar'] = array('size' => 1000, 'unencrypted_size' => 900, 'encrypted' => 1, 'mtime' => 20, 'mimetype' => 'foo/file'); + $fileData['foo'] = array('size' => 20, 'unencrypted_size' => 16, 'encrypted' => 1, 'mtime' => 25, 'mimetype' => 'foo/file'); + + $this->cache->put($file1, $data1); + $this->cache->put($file2, $fileData['bar']); + $this->cache->put($file3, $fileData['foo']); + + $content = $this->cache->getFolderContents($file1); + $this->assertEquals(count($content), 2); + foreach ($content as $cachedData) { + $data = $fileData[$cachedData['name']]; + // indirect retrieval swaps unencrypted_size and size + $this->assertEquals($data['unencrypted_size'], $cachedData['size']); + } + + $file4 = 'folder/unkownSize'; + $fileData['unkownSize'] = array('size' => -1, 'mtime' => 25, 'mimetype' => 'foo/file'); + $this->cache->put($file4, $fileData['unkownSize']); + + $this->assertEquals(-1, $this->cache->calculateFolderSize($file1)); + + $fileData['unkownSize'] = array('size' => 5, 'mtime' => 25, 'mimetype' => 'foo/file'); + $this->cache->put($file4, $fileData['unkownSize']); + + $this->assertEquals(916, $this->cache->calculateFolderSize($file1)); + // direct cache entry retrieval returns the original values + $this->assertEquals(1025, $this->cache->get($file1)['size']); + $this->assertEquals(916, $this->cache->get($file1)['unencrypted_size']); + + $this->cache->remove($file2); + $this->cache->remove($file3); + $this->cache->remove($file4); + $this->assertEquals(0, $this->cache->calculateFolderSize($file1)); + + $this->cache->remove('folder'); + $this->assertFalse($this->cache->inCache('folder/foo')); + $this->assertFalse($this->cache->inCache('folder/bar')); + } + public function testRootFolderSizeForNonHomeStorage() { $dir1 = 'knownsize'; $dir2 = 'unknownsize'; -- cgit v1.2.3 From 203d5d01cabae52373f556d50f2bb541560eb4b0 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 14 Jan 2014 13:54:07 +0100 Subject: Use storage_mtime when determining if we can reuse cached data while scanning --- lib/private/files/cache/scanner.php | 2 +- tests/lib/files/cache/updater.php | 2 +- tests/lib/files/etagtest.php | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) (limited to 'lib/private/files') diff --git a/lib/private/files/cache/scanner.php b/lib/private/files/cache/scanner.php index a8c069ee99f..92a4c01841b 100644 --- a/lib/private/files/cache/scanner.php +++ b/lib/private/files/cache/scanner.php @@ -122,7 +122,7 @@ class Scanner extends BasicEmitter { $propagateETagChange = true; } // only reuse data if the file hasn't explicitly changed - if (isset($data['mtime']) && isset($cacheData['mtime']) && $data['mtime'] === $cacheData['mtime']) { + if (isset($data['storage_mtime']) && isset($cacheData['storage_mtime']) && $data['storage_mtime'] === $cacheData['storage_mtime']) { if (($reuseExisting & self::REUSE_SIZE) && ($data['size'] === -1)) { $data['size'] = $cacheData['size']; } diff --git a/tests/lib/files/cache/updater.php b/tests/lib/files/cache/updater.php index 91e384e12af..ba103cee675 100644 --- a/tests/lib/files/cache/updater.php +++ b/tests/lib/files/cache/updater.php @@ -88,7 +88,7 @@ class Updater extends \PHPUnit_Framework_TestCase { public function testWrite() { $textSize = strlen("dummy file data\n"); $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png'); - $this->cache->put('foo.txt', array('mtime' => 100)); + $this->cache->put('foo.txt', array('mtime' => 100, 'storage_mtime' => 100)); $rootCachedData = $this->cache->get(''); $this->assertEquals(3 * $textSize + $imageSize, $rootCachedData['size']); diff --git a/tests/lib/files/etagtest.php b/tests/lib/files/etagtest.php index 6c41413c4df..14003896d66 100644 --- a/tests/lib/files/etagtest.php +++ b/tests/lib/files/etagtest.php @@ -11,6 +11,12 @@ namespace Test\Files; use OC\Files\Filesystem; use OCP\Share; +class TemporaryNoTouch extends \OC\Files\Storage\Temporary { + public function touch($path, $mtime = null) { + return false; + } +} + class EtagTest extends \PHPUnit_Framework_TestCase { private $datadir; @@ -68,6 +74,23 @@ class EtagTest extends \PHPUnit_Framework_TestCase { $this->assertEquals($originalEtags, $this->getEtags($files)); } + public function testTouchNotSupported() { + $storage = new TemporaryNoTouch(array()); + $scanner = $storage->getScanner(); + Filesystem::mount($storage, array(), '/test/'); + $past = time() - 100; + $storage->file_put_contents('test', 'foobar'); + $scanner->scan(''); + $view = new \OC\Files\View(''); + $info = $view->getFileInfo('/test/test'); + + $view->touch('/test/test', $past); + $scanner->scanFile('test', \OC\Files\Cache\Scanner::REUSE_ETAG); + + $info2 = $view->getFileInfo('/test/test'); + $this->assertEquals($info['etag'], $info2['etag']); + } + private function getEtags($files) { $etags = array(); foreach ($files as $file) { -- cgit v1.2.3 From 1af7dab5358d7fd495e4382037c6e2528e2b76d5 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Sun, 19 Jan 2014 18:49:51 +0100 Subject: Fixed quota wrapper to not wrap failed fopen streams When calling fopen() on some storage types, these return false instead of throwing an exception. This fix makes sure that in case the stream wasn't opened (for example when a file doesn't exist any more) the stream isn't wrapped. Also added 'rb' as another case that doesn't need to be wrapped. Fixes #6832 --- lib/private/files/storage/wrapper/quota.php | 2 +- tests/lib/files/storage/wrapper/quota.php | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'lib/private/files') diff --git a/lib/private/files/storage/wrapper/quota.php b/lib/private/files/storage/wrapper/quota.php index 43016e0892f..a430e3e4617 100644 --- a/lib/private/files/storage/wrapper/quota.php +++ b/lib/private/files/storage/wrapper/quota.php @@ -95,7 +95,7 @@ class Quota extends Wrapper { public function fopen($path, $mode) { $source = $this->storage->fopen($path, $mode); $free = $this->free_space(''); - if ($free >= 0 && $mode !== 'r') { + if ($source && $free >= 0 && $mode !== 'r' && $mode !== 'rb') { return \OC\Files\Stream\Quota::wrap($source, $free); } else { return $source; diff --git a/tests/lib/files/storage/wrapper/quota.php b/tests/lib/files/storage/wrapper/quota.php index 9b14335782f..87bafb64d41 100644 --- a/tests/lib/files/storage/wrapper/quota.php +++ b/tests/lib/files/storage/wrapper/quota.php @@ -59,6 +59,20 @@ class Quota extends \Test\Files\Storage\Storage { $this->assertEquals('foobarqwe', $instance->file_get_contents('foo')); } + public function testReturnFalseWhenFopenFailed(){ + $failStorage = $this->getMock( + '\OC\Files\Storage\Local', + array('fopen'), + array(array('datadir' => $this->tmpDir))); + $failStorage->expects($this->any()) + ->method('fopen') + ->will($this->returnValue(false)); + + $instance = new \OC\Files\Storage\Wrapper\Quota(array('storage' => $failStorage, 'quota' => 1000)); + + $this->assertFalse($instance->fopen('failedfopen', 'r')); + } + public function testReturnRegularStreamOnRead(){ $instance = $this->getLimitedStorage(9); @@ -71,6 +85,11 @@ class Quota extends \Test\Files\Storage\Storage { $meta = stream_get_meta_data($stream); $this->assertEquals('plainfile', $meta['wrapper_type']); fclose($stream); + + $stream = $instance->fopen('foo', 'rb'); + $meta = stream_get_meta_data($stream); + $this->assertEquals('plainfile', $meta['wrapper_type']); + fclose($stream); } public function testReturnQuotaStreamOnWrite(){ -- cgit v1.2.3 From 3d6d8d1bb683f9daca3a2b8a876e291adc320375 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 20 Jan 2014 15:21:21 +0100 Subject: Reuse the data retrieved from the cache in checkUpdate --- apps/files_sharing/lib/watcher.php | 2 +- lib/private/files/cache/watcher.php | 4 ++-- lib/private/files/view.php | 7 +++++-- 3 files changed, 8 insertions(+), 5 deletions(-) (limited to 'lib/private/files') diff --git a/apps/files_sharing/lib/watcher.php b/apps/files_sharing/lib/watcher.php index c40cf6911b8..285b1a58c6e 100644 --- a/apps/files_sharing/lib/watcher.php +++ b/apps/files_sharing/lib/watcher.php @@ -32,7 +32,7 @@ class Shared_Watcher extends Watcher { * @param string $path */ public function checkUpdate($path) { - if ($path != '' && parent::checkUpdate($path)) { + if ($path != '' && parent::checkUpdate($path) === true) { // since checkUpdate() has already updated the size of the subdirs, // only apply the update to the owner's parent dirs diff --git a/lib/private/files/cache/watcher.php b/lib/private/files/cache/watcher.php index 58f624c8990..251ecbe7071 100644 --- a/lib/private/files/cache/watcher.php +++ b/lib/private/files/cache/watcher.php @@ -40,7 +40,7 @@ class Watcher { * check $path for updates * * @param string $path - * @return boolean true if path was updated, false otherwise + * @return boolean | array true if path was updated, otherwise the cached data is returned */ public function checkUpdate($path) { $cachedEntry = $this->cache->get($path); @@ -56,7 +56,7 @@ class Watcher { $this->cache->correctFolderSize($path); return true; } - return false; + return $cachedEntry; } /** diff --git a/lib/private/files/view.php b/lib/private/files/view.php index 8893911ed5d..d97544b865e 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -801,6 +801,7 @@ class View { * @var string $internalPath */ list($storage, $internalPath) = Filesystem::resolvePath($path); + $data = null; if ($storage) { $cache = $storage->getCache($internalPath); $permissionsCache = $storage->getPermissionsCache($internalPath); @@ -811,10 +812,12 @@ class View { $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); } else { $watcher = $storage->getWatcher($internalPath); - $watcher->checkUpdate($internalPath); + $data = $watcher->checkUpdate($internalPath); } - $data = $cache->get($internalPath); + if (!is_array($data)) { + $data = $cache->get($internalPath); + } if ($data and $data['fileid']) { if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') { -- cgit v1.2.3