2012-10-28 06:26:31 -04:00
|
|
|
<?php
|
2024-05-23 03:26:56 -04:00
|
|
|
|
2012-10-28 06:26:31 -04:00
|
|
|
/**
|
2024-05-23 03:26:56 -04:00
|
|
|
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
|
|
|
|
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
|
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
2012-10-28 06:26:31 -04:00
|
|
|
*/
|
|
|
|
|
namespace OC\Files\Cache;
|
2020-04-09 05:48:10 -04:00
|
|
|
|
2025-11-20 12:55:01 -05:00
|
|
|
use OCP\Files\Cache\ICache;
|
2015-12-02 09:14:40 -05:00
|
|
|
use OCP\Files\Cache\ICacheEntry;
|
2025-11-20 12:55:01 -05:00
|
|
|
use OCP\Files\Cache\IScanner;
|
2015-12-02 09:14:40 -05:00
|
|
|
use OCP\Files\Cache\IWatcher;
|
2025-11-20 12:55:01 -05:00
|
|
|
use OCP\Files\Storage\IStorage;
|
2012-10-28 06:26:31 -04:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* check the storage backends for updates and change the cache accordingly
|
|
|
|
|
*/
|
2015-12-02 09:14:40 -05:00
|
|
|
class Watcher implements IWatcher {
|
2014-02-18 10:31:40 -05:00
|
|
|
protected $watchPolicy = self::CHECK_ONCE;
|
|
|
|
|
|
2020-03-26 04:30:18 -04:00
|
|
|
protected $checkedPaths = [];
|
2014-02-18 10:31:40 -05:00
|
|
|
|
2012-10-28 06:26:31 -04:00
|
|
|
/**
|
2025-11-20 12:55:01 -05:00
|
|
|
* @var IStorage $storage
|
2012-10-28 06:26:31 -04:00
|
|
|
*/
|
2013-11-05 07:58:14 -05:00
|
|
|
protected $storage;
|
2012-10-28 06:26:31 -04:00
|
|
|
|
|
|
|
|
/**
|
2025-11-20 12:55:01 -05:00
|
|
|
* @var ICache $cache
|
2012-10-28 06:26:31 -04:00
|
|
|
*/
|
2013-11-05 07:58:14 -05:00
|
|
|
protected $cache;
|
2012-10-28 06:26:31 -04:00
|
|
|
|
|
|
|
|
/**
|
2025-11-20 12:55:01 -05:00
|
|
|
* @var IScanner $scanner ;
|
2012-10-28 06:26:31 -04:00
|
|
|
*/
|
2013-11-05 07:58:14 -05:00
|
|
|
protected $scanner;
|
2012-10-28 06:26:31 -04:00
|
|
|
|
2025-01-22 13:24:45 -05:00
|
|
|
/** @var callable[] */
|
|
|
|
|
protected $onUpdate = [];
|
|
|
|
|
|
2025-04-25 14:23:28 -04:00
|
|
|
protected ?string $checkFilter = null;
|
|
|
|
|
|
2025-11-20 12:55:01 -05:00
|
|
|
public function __construct(IStorage $storage) {
|
2012-10-28 06:26:31 -04:00
|
|
|
$this->storage = $storage;
|
|
|
|
|
$this->cache = $storage->getCache();
|
|
|
|
|
$this->scanner = $storage->getScanner();
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-18 10:31:40 -05:00
|
|
|
/**
|
2014-05-22 07:30:32 -04:00
|
|
|
* @param int $policy either \OC\Files\Cache\Watcher::CHECK_NEVER, \OC\Files\Cache\Watcher::CHECK_ONCE, \OC\Files\Cache\Watcher::CHECK_ALWAYS
|
2014-02-18 10:31:40 -05:00
|
|
|
*/
|
|
|
|
|
public function setPolicy($policy) {
|
|
|
|
|
$this->watchPolicy = $policy;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-25 14:23:28 -04:00
|
|
|
public function setCheckFilter(?string $filter): void {
|
|
|
|
|
$this->checkFilter = $filter;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-05 11:27:10 -05:00
|
|
|
/**
|
|
|
|
|
* @return int either \OC\Files\Cache\Watcher::CHECK_NEVER, \OC\Files\Cache\Watcher::CHECK_ONCE, \OC\Files\Cache\Watcher::CHECK_ALWAYS
|
|
|
|
|
*/
|
|
|
|
|
public function getPolicy() {
|
|
|
|
|
return $this->watchPolicy;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-28 06:26:31 -04:00
|
|
|
/**
|
2015-10-26 10:01:58 -04:00
|
|
|
* check $path for updates and update if needed
|
2012-10-28 06:26:31 -04:00
|
|
|
*
|
|
|
|
|
* @param string $path
|
2015-12-02 09:14:40 -05:00
|
|
|
* @param ICacheEntry|null $cachedEntry
|
2014-10-02 11:37:33 -04:00
|
|
|
* @return boolean true if path was updated
|
2012-10-28 06:26:31 -04:00
|
|
|
*/
|
2014-10-02 11:37:33 -04:00
|
|
|
public function checkUpdate($path, $cachedEntry = null) {
|
2015-10-26 10:01:58 -04:00
|
|
|
if (is_null($cachedEntry)) {
|
|
|
|
|
$cachedEntry = $this->cache->get($path);
|
|
|
|
|
}
|
2019-11-22 07:37:42 -05:00
|
|
|
if ($cachedEntry === false || $this->needsUpdate($path, $cachedEntry)) {
|
2015-10-26 10:01:58 -04:00
|
|
|
$this->update($path, $cachedEntry);
|
2021-10-15 10:03:18 -04:00
|
|
|
|
|
|
|
|
if ($cachedEntry === false) {
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
// storage backends can sometimes return false positives, only return true if the scanner actually found a change
|
|
|
|
|
$newEntry = $this->cache->get($path);
|
|
|
|
|
return $newEntry->getStorageMTime() > $cachedEntry->getStorageMTime();
|
|
|
|
|
}
|
2014-02-18 10:31:40 -05:00
|
|
|
} else {
|
|
|
|
|
return false;
|
2012-10-28 06:26:31 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-26 10:01:58 -04:00
|
|
|
/**
|
|
|
|
|
* Update the cache for changes to $path
|
|
|
|
|
*
|
|
|
|
|
* @param string $path
|
2015-12-02 09:14:40 -05:00
|
|
|
* @param ICacheEntry $cachedData
|
2015-10-26 10:01:58 -04:00
|
|
|
*/
|
|
|
|
|
public function update($path, $cachedData) {
|
|
|
|
|
if ($this->storage->is_dir($path)) {
|
|
|
|
|
$this->scanner->scan($path, Scanner::SCAN_SHALLOW);
|
|
|
|
|
} else {
|
|
|
|
|
$this->scanner->scanFile($path);
|
|
|
|
|
}
|
2019-11-22 07:37:42 -05:00
|
|
|
if (is_array($cachedData) && $cachedData['mimetype'] === 'httpd/unix-directory') {
|
2015-10-26 10:01:58 -04:00
|
|
|
$this->cleanFolder($path);
|
|
|
|
|
}
|
2016-04-15 11:33:02 -04:00
|
|
|
if ($this->cache instanceof Cache) {
|
|
|
|
|
$this->cache->correctFolderSize($path);
|
|
|
|
|
}
|
2025-01-22 13:24:45 -05:00
|
|
|
foreach ($this->onUpdate as $callback) {
|
|
|
|
|
$callback($path);
|
|
|
|
|
}
|
2015-10-26 10:01:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if the cache for $path needs to be updated
|
|
|
|
|
*
|
|
|
|
|
* @param string $path
|
2015-12-02 09:14:40 -05:00
|
|
|
* @param ICacheEntry $cachedData
|
2015-10-26 10:01:58 -04:00
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function needsUpdate($path, $cachedData) {
|
2025-04-25 14:23:28 -04:00
|
|
|
if ($this->checkFilter !== null) {
|
|
|
|
|
if (!preg_match($this->checkFilter, $path)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-27 16:56:38 -04:00
|
|
|
if ($this->watchPolicy === self::CHECK_ALWAYS || ($this->watchPolicy === self::CHECK_ONCE && !in_array($path, $this->checkedPaths))) {
|
2015-11-04 10:34:39 -05:00
|
|
|
$this->checkedPaths[] = $path;
|
2025-04-04 07:41:32 -04:00
|
|
|
return $cachedData['storage_mtime'] === null || $this->storage->hasUpdated($path, $cachedData['storage_mtime']);
|
2015-10-26 10:01:58 -04:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-28 06:26:31 -04:00
|
|
|
/**
|
|
|
|
|
* remove deleted files in $path from the cache
|
|
|
|
|
*
|
|
|
|
|
* @param string $path
|
|
|
|
|
*/
|
|
|
|
|
public function cleanFolder($path) {
|
|
|
|
|
$cachedContent = $this->cache->getFolderContents($path);
|
|
|
|
|
foreach ($cachedContent as $entry) {
|
|
|
|
|
if (!$this->storage->file_exists($entry['path'])) {
|
|
|
|
|
$this->cache->remove($entry['path']);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-01-22 13:24:45 -05:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* register a callback to be called whenever the watcher triggers and update
|
|
|
|
|
*/
|
|
|
|
|
public function onUpdate(callable $callback): void {
|
|
|
|
|
$this->onUpdate[] = $callback;
|
|
|
|
|
}
|
2012-10-28 06:26:31 -04:00
|
|
|
}
|