From eca57be3367d9ea462f54cfb227701c8524a6764 Mon Sep 17 00:00:00 2001
From: Robin Appelman <icewind@owncloud.com>
Date: Wed, 18 May 2016 15:06:15 +0200
Subject: [PATCH] Only recurse into incomplete folders during background scans

---
 lib/private/Files/Cache/Scanner.php   |  9 ++++++---
 lib/public/Files/Cache/IScanner.php   |  1 +
 tests/lib/Files/Cache/ScannerTest.php | 27 +++++++++++++++++++++++++++
 3 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/lib/private/Files/Cache/Scanner.php b/lib/private/Files/Cache/Scanner.php
index fd309a4ac45..06cc6426c59 100644
--- a/lib/private/Files/Cache/Scanner.php
+++ b/lib/private/Files/Cache/Scanner.php
@@ -371,7 +371,7 @@ class Scanner extends BasicEmitter implements IScanner {
 		$childQueue = $this->handleChildren($path, $recursive, $reuse, $folderId, $lock, $size);
 
 		foreach ($childQueue as $child => $childId) {
-			$childSize = $this->scanChildren($child, self::SCAN_RECURSIVE, $reuse, $childId, $lock);
+			$childSize = $this->scanChildren($child, $recursive, $reuse, $childId, $lock);
 			if ($childSize === -1) {
 				$size = -1;
 			} else if ($size !== -1) {
@@ -402,6 +402,9 @@ class Scanner extends BasicEmitter implements IScanner {
 				if ($data) {
 					if ($data['mimetype'] === 'httpd/unix-directory' and $recursive === self::SCAN_RECURSIVE) {
 						$childQueue[$child] = $data['fileid'];
+					} else if ($data['mimetype'] === 'httpd/unix-directory' and $recursive === self::SCAN_RECURSIVE_INCOMPLETE and $data['size'] === -1) {
+						// only recurse into folders which aren't fully scanned
+						$childQueue[$child] = $data['fileid'];
 					} else if ($data['size'] === -1) {
 						$size = -1;
 					} else if ($size !== -1) {
@@ -469,8 +472,8 @@ class Scanner extends BasicEmitter implements IScanner {
 		} else {
 			$lastPath = null;
 			while (($path = $this->cache->getIncomplete()) !== false && $path !== $lastPath) {
-				$this->runBackgroundScanJob(function () use ($path) {
-					$this->scan($path, self::SCAN_RECURSIVE, self::REUSE_ETAG);
+				$this->runBackgroundScanJob(function() use ($path) {
+					$this->scan($path, self::SCAN_RECURSIVE_INCOMPLETE, self::REUSE_ETAG | self::REUSE_SIZE);
 				}, $path);
 				// FIXME: this won't proceed with the next item, needs revamping of getIncomplete()
 				// to make this possible
diff --git a/lib/public/Files/Cache/IScanner.php b/lib/public/Files/Cache/IScanner.php
index ce1f408028c..74bc2fc8843 100644
--- a/lib/public/Files/Cache/IScanner.php
+++ b/lib/public/Files/Cache/IScanner.php
@@ -27,6 +27,7 @@ namespace OCP\Files\Cache;
  * @since 9.0.0
  */
 interface IScanner {
+	const SCAN_RECURSIVE_INCOMPLETE = 2; // only recursive into not fully scanned folders
 	const SCAN_RECURSIVE = true;
 	const SCAN_SHALLOW = false;
 
diff --git a/tests/lib/Files/Cache/ScannerTest.php b/tests/lib/Files/Cache/ScannerTest.php
index 4a93f3ee014..b44b6f5d0f5 100644
--- a/tests/lib/Files/Cache/ScannerTest.php
+++ b/tests/lib/Files/Cache/ScannerTest.php
@@ -7,6 +7,7 @@
  */
 
 namespace Test\Files\Cache;
+
 use OC\Files\Cache\CacheEntry;
 
 /**
@@ -150,6 +151,32 @@ class ScannerTest extends \Test\TestCase {
 		$this->assertFalse($this->cache->getIncomplete());
 	}
 
+	function testBackgroundScanOnlyRecurseIncomplete() {
+		$this->fillTestFolders();
+		$this->storage->mkdir('folder2');
+		$this->storage->file_put_contents('folder2/bar.txt', 'foobar');
+
+		$this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW);
+		$this->assertFalse($this->cache->inCache('folder/bar.txt'));
+		$this->assertFalse($this->cache->inCache('folder/2bar.txt'));
+		$this->assertFalse($this->cache->inCache('folder2/bar.txt'));
+		$this->cache->put('folder2', ['size' => 1]); // mark as complete
+
+		$cachedData = $this->cache->get('');
+		$this->assertEquals(-1, $cachedData['size']);
+
+		$this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE_INCOMPLETE, \OC\Files\Cache\Scanner::REUSE_ETAG | \OC\Files\Cache\Scanner::REUSE_SIZE);
+
+		$this->assertTrue($this->cache->inCache('folder/bar.txt'));
+		$this->assertTrue($this->cache->inCache('folder/bar.txt'));
+		$this->assertFalse($this->cache->inCache('folder2/bar.txt'));
+
+		$cachedData = $this->cache->get('');
+		$this->assertNotEquals(-1, $cachedData['size']);
+
+		$this->assertFalse($this->cache->getIncomplete());
+	}
+
 	public function testReuseExisting() {
 		$this->fillTestFolders();
 
-- 
GitLab