diff --git a/lib/private/Files/Cache/Watcher.php b/lib/private/Files/Cache/Watcher.php index dfab51be38b29..891fe762eb896 100644 --- a/lib/private/Files/Cache/Watcher.php +++ b/lib/private/Files/Cache/Watcher.php @@ -7,8 +7,11 @@ */ namespace OC\Files\Cache; +use OCP\Files\Cache\ICache; use OCP\Files\Cache\ICacheEntry; +use OCP\Files\Cache\IScanner; use OCP\Files\Cache\IWatcher; +use OCP\Files\Storage\IStorage; /** * check the storage backends for updates and change the cache accordingly @@ -19,27 +22,26 @@ class Watcher implements IWatcher { protected $checkedPaths = []; /** - * @var \OC\Files\Storage\Storage $storage + * @var IStorage $storage */ protected $storage; /** - * @var Cache $cache + * @var ICache $cache */ protected $cache; /** - * @var Scanner $scanner ; + * @var IScanner $scanner ; */ protected $scanner; /** @var callable[] */ protected $onUpdate = []; - /** - * @param \OC\Files\Storage\Storage $storage - */ - public function __construct(\OC\Files\Storage\Storage $storage) { + protected ?string $checkFilter = null; + + public function __construct(IStorage $storage) { $this->storage = $storage; $this->cache = $storage->getCache(); $this->scanner = $storage->getScanner(); @@ -52,6 +54,10 @@ public function setPolicy($policy) { $this->watchPolicy = $policy; } + public function setCheckFilter(?string $filter): void { + $this->checkFilter = $filter; + } + /** * @return int either \OC\Files\Cache\Watcher::CHECK_NEVER, \OC\Files\Cache\Watcher::CHECK_ONCE, \OC\Files\Cache\Watcher::CHECK_ALWAYS */ @@ -116,6 +122,12 @@ public function update($path, $cachedData) { * @return bool */ public function needsUpdate($path, $cachedData) { + if ($this->checkFilter !== null) { + if (!preg_match($this->checkFilter, $path)) { + return false; + } + } + if ($this->watchPolicy === self::CHECK_ALWAYS || ($this->watchPolicy === self::CHECK_ONCE && !in_array($path, $this->checkedPaths))) { $this->checkedPaths[] = $path; return $cachedData['storage_mtime'] === null || $this->storage->hasUpdated($path, $cachedData['storage_mtime']); diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php index 1a2aeb0e0210f..bea7430bd1ec3 100644 --- a/lib/private/Files/Storage/Common.php +++ b/lib/private/Files/Storage/Common.php @@ -331,6 +331,7 @@ public function getWatcher(string $path = '', ?IStorage $storage = null): IWatch $this->watcher = new Watcher($storage); $globalPolicy = Server::get(IConfig::class)->getSystemValueInt('filesystem_check_changes', Watcher::CHECK_NEVER); $this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy)); + $this->watcher->setCheckFilter($this->getMountOption('filesystem_check_filter')); } return $this->watcher; } diff --git a/lib/public/Files/Cache/IWatcher.php b/lib/public/Files/Cache/IWatcher.php index 62b90f672c638..55063eec817ed 100644 --- a/lib/public/Files/Cache/IWatcher.php +++ b/lib/public/Files/Cache/IWatcher.php @@ -34,6 +34,17 @@ interface IWatcher { */ public function setPolicy($policy); + /** + * Set a filter regex, only paths matching the regex will be checked for updates. + * + * When set to `null`, every path will be checked for updates + * + * @param ?string $filter + * @return void + * @since 33.0.0 + */ + public function setCheckFilter(?string $filter): void; + /** * @return int either IWatcher::CHECK_NEVER, IWatcher::CHECK_ONCE, IWatcher::CHECK_ALWAYS * @since 9.0.0 diff --git a/tests/lib/Files/Cache/WatcherTest.php b/tests/lib/Files/Cache/WatcherTest.php index a7fa5086b7b15..95ba5787ef335 100644 --- a/tests/lib/Files/Cache/WatcherTest.php +++ b/tests/lib/Files/Cache/WatcherTest.php @@ -8,9 +8,13 @@ namespace Test\Files\Cache; +use OC\Files\Cache\CacheEntry; use OC\Files\Cache\Watcher; use OC\Files\Storage\Storage; use OC\Files\Storage\Temporary; +use OCP\Files\Cache\IWatcher; +use OCP\Files\Storage\IStorage; +use PHPUnit\Framework\Attributes\DataProvider; /** * Class WatcherTest @@ -192,4 +196,43 @@ private function getTestStorage($scan = true) { $this->storages[] = $storage; return $storage; } + + public static function checkFilterProvider(): array { + return [ + [null, [ + '' => true, + 'foo' => true, + 'foo.txt' => true, + ]], + ['/^.+$/', [ + '' => false, + 'foo' => true, + 'foo.txt' => true, + ]], + ['/^.+\..+$/', [ + '' => false, + 'foo' => false, + 'foo.txt' => true, + ]] + ]; + } + + #[DataProvider('checkFilterProvider')] + public function testCheckFilter($filter, $paths) { + $storage = $this->createMock(IStorage::class); + $storage->method('hasUpdated') + ->willReturn(true); + $watcher = new Watcher($storage); + $watcher->setPolicy(IWatcher::CHECK_ALWAYS); + + $watcher->setCheckFilter($filter); + + $entry = new CacheEntry([ + 'storage_mtime' => 0, + ]); + + foreach ($paths as $patch => $shouldUpdate) { + $this->assertEquals($shouldUpdate, $watcher->needsUpdate($patch, $entry)); + } + } }