From: Robin Appelman Date: Wed, 1 Apr 2015 13:12:59 +0000 (+0200) Subject: preserve cache data when doing a cross storage move X-Git-Tag: v8.1.0alpha1~19^2~6 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=d7b3a1a35a47dd37f68f7819aa0789b794e352e5;p=nextcloud-server.git preserve cache data when doing a cross storage move --- diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php index c5e118946e5..fd4c5715a67 100644 --- a/lib/private/files/cache/cache.php +++ b/lib/private/files/cache/cache.php @@ -463,6 +463,43 @@ class Cache { \OC_DB::executeAudited($sql, array($target, md5($target), basename($target), $newParentId, $sourceId)); } + /** + * Move a file or folder in the cache + * + * @param \OC\Files\Cache\Cache $sourceCache + * @param string $sourcePath + * @param string $targetPath + * @throws \OC\DatabaseException + */ + public function moveFromCache(Cache $sourceCache, $sourcePath, $targetPath) { + // normalize source and target + $sourcePath = $this->normalize($sourcePath); + $targetPath = $this->normalize($targetPath); + + $sourceData = $sourceCache->get($sourcePath); + $sourceId = $sourceData['fileid']; + $newParentId = $this->getParentId($targetPath); + + if ($sourceData['mimetype'] === 'httpd/unix-directory') { + //find all child entries + $sql = 'SELECT `path`, `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path` LIKE ?'; + $result = \OC_DB::executeAudited($sql, [$sourceCache->getNumericStorageId(), $sourcePath . '/%']); + $childEntries = $result->fetchAll(); + $sourceLength = strlen($sourcePath); + \OC_DB::beginTransaction(); + $query = \OC_DB::prepare('UPDATE `*PREFIX*filecache` SET `storage` = ?, `path` = ?, `path_hash` = ? WHERE `fileid` = ?'); + + foreach ($childEntries as $child) { + $newTargetPath = $targetPath . substr($child['path'], $sourceLength); + \OC_DB::executeAudited($query, [$this->getNumericStorageId(), $newTargetPath, md5($newTargetPath), $child['fileid']]); + } + \OC_DB::commit(); + } + + $sql = 'UPDATE `*PREFIX*filecache` SET `storage` = ?, `path` = ?, `path_hash` = ?, `name` = ?, `parent` =? WHERE `fileid` = ?'; + \OC_DB::executeAudited($sql, [$this->getNumericStorageId(), $targetPath, md5($targetPath), basename($targetPath), $newParentId, $sourceId]); + } + /** * remove all entries for files that are stored on the storage from the cache */ diff --git a/lib/private/files/cache/updater.php b/lib/private/files/cache/updater.php index 5d2ac608cfd..14aecf01220 100644 --- a/lib/private/files/cache/updater.php +++ b/lib/private/files/cache/updater.php @@ -144,30 +144,29 @@ class Updater { list($targetStorage, $targetInternalPath) = $this->view->resolvePath($target); if ($sourceStorage && $targetStorage) { + $targetCache = $targetStorage->getCache($sourceInternalPath); + if ($targetCache->inCache($targetInternalPath)) { + $targetCache->remove($targetInternalPath); + } if ($sourceStorage === $targetStorage) { - $cache = $sourceStorage->getCache($sourceInternalPath); - if ($cache->inCache($targetInternalPath)) { - $cache->remove($targetInternalPath); - } - $cache->move($sourceInternalPath, $targetInternalPath); - - if (pathinfo($sourceInternalPath, PATHINFO_EXTENSION) !== pathinfo($targetInternalPath, PATHINFO_EXTENSION)) { - // handle mime type change - $mimeType = $sourceStorage->getMimeType($targetInternalPath); - $fileId = $cache->getId($targetInternalPath); - $cache->update($fileId, array('mimetype' => $mimeType)); - } - - $cache->correctFolderSize($sourceInternalPath); - $cache->correctFolderSize($targetInternalPath); - $this->correctParentStorageMtime($sourceStorage, $sourceInternalPath); - $this->correctParentStorageMtime($targetStorage, $targetInternalPath); - $this->propagator->addChange($source); - $this->propagator->addChange($target); + $targetCache->move($sourceInternalPath, $targetInternalPath); } else { - $this->remove($source); - $this->update($target); + $targetCache->moveFromCache($sourceStorage->getCache(), $sourceInternalPath, $targetInternalPath); + } + + if (pathinfo($sourceInternalPath, PATHINFO_EXTENSION) !== pathinfo($targetInternalPath, PATHINFO_EXTENSION)) { + // handle mime type change + $mimeType = $sourceStorage->getMimeType($targetInternalPath); + $fileId = $targetCache->getId($targetInternalPath); + $targetCache->update($fileId, array('mimetype' => $mimeType)); } + + $targetCache->correctFolderSize($sourceInternalPath); + $targetCache->correctFolderSize($targetInternalPath); + $this->correctParentStorageMtime($sourceStorage, $sourceInternalPath); + $this->correctParentStorageMtime($targetStorage, $targetInternalPath); + $this->propagator->addChange($source); + $this->propagator->addChange($target); $this->propagator->propagateChanges(); } } diff --git a/tests/lib/files/cache/updater.php b/tests/lib/files/cache/updater.php index 7c3ebd5a6f9..726ee360479 100644 --- a/tests/lib/files/cache/updater.php +++ b/tests/lib/files/cache/updater.php @@ -172,4 +172,78 @@ class Updater extends \Test\TestCase { $this->assertTrue($this->cache->inCache('foo.txt')); $this->assertFalse($this->cache->inCache('bar.txt')); } + + public function testMoveCrossStorage() { + $storage2 = new Temporary(array()); + $cache2 = $storage2->getCache(); + Filesystem::mount($storage2, array(), '/bar'); + $this->storage->file_put_contents('foo.txt', 'qwerty'); + + $this->updater->update('foo.txt'); + + $this->assertTrue($this->cache->inCache('foo.txt')); + $this->assertFalse($cache2->inCache('bar.txt')); + $cached = $this->cache->get('foo.txt'); + + // "rename" + $storage2->file_put_contents('bar.txt', 'qwerty'); + $this->storage->unlink('foo.txt'); + + $this->assertTrue($this->cache->inCache('foo.txt')); + $this->assertFalse($cache2->inCache('bar.txt')); + + $this->updater->rename('foo.txt', 'bar/bar.txt'); + + $this->assertFalse($this->cache->inCache('foo.txt')); + $this->assertTrue($cache2->inCache('bar.txt')); + + $cachedTarget = $cache2->get('bar.txt'); + $this->assertEquals($cached['mtime'], $cachedTarget['mtime']); + $this->assertEquals($cached['size'], $cachedTarget['size']); + $this->assertEquals($cached['etag'], $cachedTarget['etag']); + $this->assertEquals($cached['fileid'], $cachedTarget['fileid']); + } + + public function testMoveFolderCrossStorage() { + $storage2 = new Temporary(array()); + $cache2 = $storage2->getCache(); + Filesystem::mount($storage2, array(), '/bar'); + $this->storage->mkdir('foo'); + $this->storage->mkdir('foo/bar'); + $this->storage->file_put_contents('foo/foo.txt', 'qwerty'); + $this->storage->file_put_contents('foo/bar.txt', 'foo'); + $this->storage->file_put_contents('foo/bar/bar.txt', 'qwertyuiop'); + + $this->storage->getScanner()->scan(''); + + $this->assertTrue($this->cache->inCache('foo/foo.txt')); + $this->assertTrue($this->cache->inCache('foo/bar.txt')); + $this->assertTrue($this->cache->inCache('foo/bar/bar.txt')); + $cached = []; + $cached[] = $this->cache->get('foo/foo.txt'); + $cached[] = $this->cache->get('foo/bar.txt'); + $cached[] = $this->cache->get('foo/bar/bar.txt'); + + $this->view->rename('/foo', '/bar/foo'); + + $this->assertFalse($this->cache->inCache('foo/foo.txt')); + $this->assertFalse($this->cache->inCache('foo/bar.txt')); + $this->assertFalse($this->cache->inCache('foo/bar/bar.txt')); + $this->assertTrue($cache2->inCache('foo/foo.txt')); + $this->assertTrue($cache2->inCache('foo/bar.txt')); + $this->assertTrue($cache2->inCache('foo/bar/bar.txt')); + + $cachedTarget = []; + $cachedTarget[] = $cache2->get('foo/foo.txt'); + $cachedTarget[] = $cache2->get('foo/bar.txt'); + $cachedTarget[] = $cache2->get('foo/bar/bar.txt'); + + foreach ($cached as $i => $old) { + $new = $cachedTarget[$i]; + $this->assertEquals($old['mtime'], $new['mtime']); + $this->assertEquals($old['size'], $new['size']); + $this->assertEquals($old['etag'], $new['etag']); + $this->assertEquals($old['fileid'], $new['fileid']); + } + } }