diff --git a/db_structure.xml b/db_structure.xml index dce90697b1..b96d483641 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -280,6 +280,14 @@ 4 + + storage_mtime + integer + + true + 4 + + encrypted integer diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 8f5c9643be..0617471079 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -107,7 +107,7 @@ class Cache { $params = array($file); } $query = \OC_DB::prepare( - 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag` + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, `encrypted`, `etag` FROM `*PREFIX*filecache` ' . $where); $result = $query->execute($params); $data = $result->fetchRow(); @@ -126,6 +126,9 @@ class Cache { $data['storage'] = $this->storageId; $data['mimetype'] = $this->getMimetype($data['mimetype']); $data['mimepart'] = $this->getMimetype($data['mimepart']); + if ($data['storage_mtime'] == 0) { + $data['storage_mtime'] = $data['mtime']; + } } return $data; @@ -141,13 +144,16 @@ class Cache { $fileId = $this->getId($folder); if ($fileId > -1) { $query = \OC_DB::prepare( - 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag` + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, `encrypted`, `etag` FROM `*PREFIX*filecache` WHERE parent = ? ORDER BY `name` ASC'); $result = $query->execute(array($fileId)); $files = $result->fetchAll(); foreach ($files as &$file) { $file['mimetype'] = $this->getMimetype($file['mimetype']); $file['mimepart'] = $this->getMimetype($file['mimepart']); + if ($file['storage_mtime'] == 0) { + $file['storage_mtime'] = $file['mtime']; + } } return $files; } else { @@ -224,7 +230,7 @@ class Cache { * @return array */ function buildParts(array $data) { - $fields = array('path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'encrypted', 'etag'); + $fields = array('path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted', 'etag'); $params = array(); $queryParts = array(); foreach ($data as $name => $value) { @@ -236,6 +242,11 @@ class Cache { $params[] = $this->getMimetypeId(substr($value, 0, strpos($value, '/'))); $queryParts[] = '`mimepart`'; $value = $this->getMimetypeId($value); + } elseif ($name === 'storage_mtime') { + if (!isset($data['mtime'])) { + $params[] = $value; + $queryParts[] = '`mtime`'; + } } $params[] = $value; $queryParts[] = '`' . $name . '`'; diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index b99dea23cb..a98953b42a 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -51,6 +51,7 @@ class Scanner { $data['size'] = $this->storage->filesize($path); } $data['etag'] = $this->storage->getETag($path); + $data['storage_mtime'] = $data['mtime']; return $data; } diff --git a/lib/files/cache/watcher.php b/lib/files/cache/watcher.php index 31059ec7f5..8bfd4602f3 100644 --- a/lib/files/cache/watcher.php +++ b/lib/files/cache/watcher.php @@ -43,7 +43,7 @@ class Watcher { */ public function checkUpdate($path) { $cachedEntry = $this->cache->get($path); - if ($this->storage->hasUpdated($path, $cachedEntry['mtime'])) { + if ($this->storage->hasUpdated($path, $cachedEntry['storage_mtime'])) { if ($this->storage->is_dir($path)) { $this->scanner->scan($path, Scanner::SCAN_SHALLOW); } else { diff --git a/lib/files/view.php b/lib/files/view.php index f35e1e3dc1..bc6b80c505 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -251,8 +251,11 @@ class View { if (!$this->file_exists($path)) { $hooks[] = 'write'; } - - return $this->basicOperation('touch', $path, $hooks, $mtime); + $result = $this->basicOperation('touch', $path, $hooks, $mtime); + if (!$result) { //if native touch fails, we emulate it by changing the mtime in the cache + $this->putFileInfo($path, array('mtime' => $mtime)); + } + return true; } public function file_get_contents($path) { diff --git a/lib/util.php b/lib/util.php index f30cdf6a53..48c224a303 100755 --- a/lib/util.php +++ b/lib/util.php @@ -77,7 +77,7 @@ class OC_Util { public static function getVersion() { // hint: We only can count up. Reset minor/patchlevel when // updating major/minor version number. - return array(5, 80, 02); + return array(5, 80, 03); } /** diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 4051a6e234..1612a67383 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -211,6 +211,23 @@ class Cache extends \PHPUnit_Framework_TestCase { $this->assertEquals(array($storageId, 'foo'), \OC\Files\Cache\Cache::getById($id)); } + function testStorageMTime() { + $data = array('size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'); + $this->cache->put('foo', $data); + $cachedData = $this->cache->get('foo'); + $this->assertEquals($data['mtime'], $cachedData['storage_mtime']);//if no storage_mtime is saved, mtime should be used + + $this->cache->put('foo', array('storage_mtime' => 30));//when setting storage_mtime, mtime is also set + $cachedData = $this->cache->get('foo'); + $this->assertEquals(30, $cachedData['storage_mtime']); + $this->assertEquals(30, $cachedData['mtime']); + + $this->cache->put('foo', array('mtime' => 25));//setting mtime does not change storage_mtime + $cachedData = $this->cache->get('foo'); + $this->assertEquals(30, $cachedData['storage_mtime']); + $this->assertEquals(25, $cachedData['mtime']); + } + function testLongId() { $storage = new LongId(array()); $cache = $storage->getCache(); diff --git a/tests/lib/files/cache/watcher.php b/tests/lib/files/cache/watcher.php index 8ef6ab44d1..e43c86ed43 100644 --- a/tests/lib/files/cache/watcher.php +++ b/tests/lib/files/cache/watcher.php @@ -35,7 +35,7 @@ class Watcher extends \PHPUnit_Framework_TestCase { $updater = $storage->getWatcher(); //set the mtime to the past so it can detect an mtime change - $cache->put('', array('mtime' => 10)); + $cache->put('', array('storage_mtime' => 10)); $this->assertTrue($cache->inCache('folder/bar.txt')); $this->assertTrue($cache->inCache('folder/bar2.txt')); @@ -47,14 +47,14 @@ class Watcher extends \PHPUnit_Framework_TestCase { $cachedData = $cache->get('bar.test'); $this->assertEquals(3, $cachedData['size']); - $cache->put('bar.test', array('mtime' => 10)); + $cache->put('bar.test', array('storage_mtime' => 10)); $storage->file_put_contents('bar.test', 'test data'); $updater->checkUpdate('bar.test'); $cachedData = $cache->get('bar.test'); $this->assertEquals(9, $cachedData['size']); - $cache->put('folder', array('mtime' => 10)); + $cache->put('folder', array('storage_mtime' => 10)); $storage->unlink('folder/bar2.txt'); $updater->checkUpdate('folder'); @@ -69,7 +69,7 @@ class Watcher extends \PHPUnit_Framework_TestCase { $updater = $storage->getWatcher(); //set the mtime to the past so it can detect an mtime change - $cache->put('', array('mtime' => 10)); + $cache->put('', array('storage_mtime' => 10)); $storage->unlink('foo.txt'); $storage->rename('folder', 'foo.txt'); @@ -85,7 +85,7 @@ class Watcher extends \PHPUnit_Framework_TestCase { $updater = $storage->getWatcher(); //set the mtime to the past so it can detect an mtime change - $cache->put('foo.txt', array('mtime' => 10)); + $cache->put('foo.txt', array('storage_mtime' => 10)); $storage->unlink('foo.txt'); $storage->rename('folder', 'foo.txt'); diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index a064e44f3e..af97761bbb 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -7,6 +7,12 @@ namespace Test\Files; +class TemporaryNoTouch extends \OC\Files\Storage\Temporary { + public function touch($path, $mtime = null) { + return false; + } +} + class View extends \PHPUnit_Framework_TestCase { /** * @var \OC\Files\Storage\Storage[] $storages; @@ -220,7 +226,7 @@ class View extends \PHPUnit_Framework_TestCase { $cachedData = $rootView->getFileInfo('foo.txt'); $this->assertEquals(16, $cachedData['size']); - $rootView->putFileInfo('foo.txt', array('mtime' => 10)); + $rootView->putFileInfo('foo.txt', array('storage_mtime' => 10)); $storage1->file_put_contents('foo.txt', 'foo'); clearstatcache(); @@ -228,12 +234,36 @@ class View extends \PHPUnit_Framework_TestCase { $this->assertEquals(3, $cachedData['size']); } + function testTouch() { + $storage = $this->getTestStorage(true, '\Test\Files\TemporaryNoTouch'); + + \OC\Files\Filesystem::mount($storage, array(), '/'); + + $rootView = new \OC\Files\View(''); + $oldCachedData = $rootView->getFileInfo('foo.txt'); + + $rootView->touch('foo.txt', 500); + + $cachedData = $rootView->getFileInfo('foo.txt'); + $this->assertEquals(500, $cachedData['mtime']); + $this->assertEquals($oldCachedData['storage_mtime'], $cachedData['storage_mtime']); + + $rootView->putFileInfo('foo.txt', array('storage_mtime' => 1000)); //make sure the watcher detects the change + $rootView->file_put_contents('foo.txt', 'asd'); + $cachedData = $rootView->getFileInfo('foo.txt'); + $this->assertGreaterThanOrEqual($cachedData['mtime'], $oldCachedData['mtime']); + $this->assertEquals($cachedData['storage_mtime'], $cachedData['mtime']); + } + /** * @param bool $scan * @return \OC\Files\Storage\Storage */ - private function getTestStorage($scan = true) { - $storage = new \OC\Files\Storage\Temporary(array()); + private function getTestStorage($scan = true, $class = '\OC\Files\Storage\Temporary') { + /** + * @var \OC\Files\Storage\Storage $storage + */ + $storage = new $class(array()); $textData = "dummy file data\n"; $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo.png'); $storage->mkdir('folder');