diff options
Diffstat (limited to 'lib/private/files')
-rw-r--r-- | lib/private/files/cache/cache.php | 16 | ||||
-rw-r--r-- | lib/private/files/cache/changepropagator.php | 24 | ||||
-rw-r--r-- | lib/private/files/cache/propagator.php | 66 | ||||
-rw-r--r-- | lib/private/files/cache/updater.php | 168 | ||||
-rw-r--r-- | lib/private/files/fileinfo.php | 36 | ||||
-rw-r--r-- | lib/private/files/storage/common.php | 30 | ||||
-rw-r--r-- | lib/private/files/storage/dav.php | 9 | ||||
-rw-r--r-- | lib/private/files/storage/storage.php | 16 | ||||
-rw-r--r-- | lib/private/files/storage/wrapper/wrapper.php | 14 | ||||
-rw-r--r-- | lib/private/files/stream/encryption.php | 4 | ||||
-rw-r--r-- | lib/private/files/view.php | 236 |
11 files changed, 392 insertions, 227 deletions
diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php index 40477243324..e6110c1925d 100644 --- a/lib/private/files/cache/cache.php +++ b/lib/private/files/cache/cache.php @@ -160,6 +160,7 @@ class Cache { } else { //fix types $data['fileid'] = (int)$data['fileid']; + $data['parent'] = (int)$data['parent']; $data['size'] = 0 + $data['size']; $data['mtime'] = (int)$data['mtime']; $data['storage_mtime'] = (int)$data['storage_mtime']; @@ -391,12 +392,17 @@ class Cache { if ($file === '') { return -1; } else { - $parent = dirname($file); - if ($parent === '.') { - $parent = ''; - } - return $this->getId($parent); + $parent = $this->getParentPath($file); + return (int) $this->getId($parent); + } + } + + private function getParentPath($path) { + $parent = dirname($path); + if ($parent === '.') { + $parent = ''; } + return $parent; } /** diff --git a/lib/private/files/cache/changepropagator.php b/lib/private/files/cache/changepropagator.php index 9696a82257e..2a48eb13c59 100644 --- a/lib/private/files/cache/changepropagator.php +++ b/lib/private/files/cache/changepropagator.php @@ -22,6 +22,7 @@ namespace OC\Files\Cache; +use OC\Files\Filesystem; use OC\Hooks\BasicEmitter; /** @@ -61,23 +62,30 @@ class ChangePropagator extends BasicEmitter { * @param int $time (optional) the mtime to set for the folders, if not set the current time is used */ public function propagateChanges($time = null) { - $parents = $this->getAllParents(); - $this->changedFiles = array(); + $changes = $this->getChanges(); + $this->changedFiles = []; if (!$time) { $time = time(); } - foreach ($parents as $parent) { + foreach ($changes as $change) { /** * @var \OC\Files\Storage\Storage $storage * @var string $internalPath */ - list($storage, $internalPath) = $this->view->resolvePath($parent); + $absolutePath = $this->view->getAbsolutePath($change); + $mount = $this->view->getMount($change); + $storage = $mount->getStorage(); + $internalPath = $mount->getInternalPath($absolutePath); if ($storage) { - $cache = $storage->getCache(); - $entry = $cache->get($internalPath); - $cache->update($entry['fileid'], array('mtime' => max($time, $entry['mtime']), 'etag' => $storage->getETag($internalPath))); - $this->emit('\OC\Files', 'propagate', [$parent, $entry]); + $propagator = $storage->getPropagator(); + $propagatedEntries = $propagator->propagateChange($internalPath, $time); + + foreach ($propagatedEntries as $entry) { + $absolutePath = Filesystem::normalizePath($mount->getMountPoint() . '/' . $entry['path']); + $relativePath = $this->view->getRelativePath($absolutePath); + $this->emit('\OC\Files', 'propagate', [$relativePath, $entry]); + } } } } diff --git a/lib/private/files/cache/propagator.php b/lib/private/files/cache/propagator.php new file mode 100644 index 00000000000..bd11cef5990 --- /dev/null +++ b/lib/private/files/cache/propagator.php @@ -0,0 +1,66 @@ +<?php +/** + * @author Robin Appelman <icewind@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Files\Cache; + +/** + * Propagate etags and mtimes within the storage + */ +class Propagator { + /** + * @var \OC\Files\Storage\Storage + */ + protected $storage; + + /** + * @param \OC\Files\Storage\Storage $storage + */ + public function __construct(\OC\Files\Storage\Storage $storage) { + $this->storage = $storage; + } + + + /** + * @param string $internalPath + * @param int $time + * @return array[] all propagated entries + */ + public function propagateChange($internalPath, $time) { + $cache = $this->storage->getCache($internalPath); + + $parentId = $cache->getParentId($internalPath); + $propagatedEntries = []; + while ($parentId !== -1) { + $entry = $cache->get($parentId); + $propagatedEntries[] = $entry; + if (!$entry) { + return $propagatedEntries; + } + $mtime = max($time, $entry['mtime']); + + $cache->update($parentId, ['mtime' => $mtime, 'etag' => $this->storage->getETag($entry['path'])]); + + $parentId = $entry['parent']; + } + + return $propagatedEntries; + } +} diff --git a/lib/private/files/cache/updater.php b/lib/private/files/cache/updater.php index 85dcc8589de..4d11fa3a251 100644 --- a/lib/private/files/cache/updater.php +++ b/lib/private/files/cache/updater.php @@ -29,8 +29,6 @@ namespace OC\Files\Cache; /** * Update the cache and propagate changes * - * Unlike most other classes an Updater is not related to a specific storage but handles updates for all storages in a users filesystem. - * This is needed because the propagation of mtime and etags need to cross storage boundaries */ class Updater { /** @@ -39,21 +37,33 @@ class Updater { protected $enabled = true; /** - * @var \OC\Files\View + * @var \OC\Files\Storage\Storage */ - protected $view; + protected $storage; /** - * @var \OC\Files\Cache\ChangePropagator + * @var \OC\Files\Cache\Propagator */ protected $propagator; /** - * @param \OC\Files\View $view the view the updater works on, usually the view of the logged in user + * @var Scanner */ - public function __construct($view) { - $this->view = $view; - $this->propagator = new ChangePropagator($view); + protected $scanner; + + /** + * @var Cache + */ + protected $cache; + + /** + * @param \OC\Files\Storage\Storage $storage + */ + public function __construct(\OC\Files\Storage\Storage $storage) { + $this->storage = $storage; + $this->propagator = $storage->getPropagator(); + $this->scanner = $storage->getScanner(); + $this->cache = $storage->getCache(); } /** @@ -73,7 +83,7 @@ class Updater { /** * Get the propagator for etags and mtime for the view the updater works on * - * @return ChangePropagator + * @return Propagator */ public function getPropagator() { return $this->propagator; @@ -89,8 +99,7 @@ class Updater { if (Scanner::isPartialFile($path)) { return; } - $this->propagator->addChange($path); - $this->propagator->propagateChanges($time); + $this->propagator->propagateChange($path, $time); } /** @@ -103,20 +112,14 @@ class Updater { if (!$this->enabled or Scanner::isPartialFile($path)) { return; } - /** - * @var \OC\Files\Storage\Storage $storage - * @var string $internalPath - */ - list($storage, $internalPath) = $this->view->resolvePath($path); - if ($storage) { - $this->propagator->addChange($path); - $cache = $storage->getCache($internalPath); - $scanner = $storage->getScanner($internalPath); - $data = $scanner->scan($internalPath, Scanner::SCAN_SHALLOW, -1, false); - $this->correctParentStorageMtime($storage, $internalPath); - $cache->correctFolderSize($internalPath, $data); - $this->propagator->propagateChanges($time); + if (is_null($time)) { + $time = time(); } + + $data = $this->scanner->scan($path, Scanner::SCAN_SHALLOW, -1, false); + $this->correctParentStorageMtime($path); + $this->cache->correctFolderSize($path, $data); + $this->propagator->propagateChange($path, $time); } /** @@ -128,87 +131,71 @@ class Updater { if (!$this->enabled or Scanner::isPartialFile($path)) { return; } - /** - * @var \OC\Files\Storage\Storage $storage - * @var string $internalPath - */ - list($storage, $internalPath) = $this->view->resolvePath($path); - if ($storage) { - $parent = dirname($internalPath); - if ($parent === '.') { - $parent = ''; - } - $this->propagator->addChange($path); - $cache = $storage->getCache($internalPath); - $cache->remove($internalPath); - $cache->correctFolderSize($parent); - $this->correctParentStorageMtime($storage, $internalPath); - $this->propagator->propagateChanges(); + + $parent = dirname($path); + if ($parent === '.') { + $parent = ''; } + + $this->cache->remove($path); + $this->cache->correctFolderSize($parent); + $this->correctParentStorageMtime($path); + $this->propagator->propagateChange($path, time()); } /** * Rename a file or folder in the cache and update the size, etag and mtime of the parent folders * + * @param \OC\Files\Storage\Storage $sourceStorage * @param string $source * @param string $target */ - public function rename($source, $target) { + public function renameFromStorage(\OC\Files\Storage\Storage $sourceStorage, $source, $target) { if (!$this->enabled or Scanner::isPartialFile($source) or Scanner::isPartialFile($target)) { return; } - /** - * @var \OC\Files\Storage\Storage $sourceStorage - * @var \OC\Files\Storage\Storage $targetStorage - * @var string $sourceInternalPath - * @var string $targetInternalPath - */ - list($sourceStorage, $sourceInternalPath) = $this->view->resolvePath($source); - // if it's a moved mountpoint we dont need to do anything - if ($sourceInternalPath === '') { - return; - } - list($targetStorage, $targetInternalPath) = $this->view->resolvePath($target); - - if ($sourceStorage && $targetStorage) { - $targetCache = $targetStorage->getCache($sourceInternalPath); - if ($sourceStorage->getCache($sourceInternalPath)->inCache($sourceInternalPath)) { - if ($targetCache->inCache($targetInternalPath)) { - $targetCache->remove($targetInternalPath); - } - if ($sourceStorage === $targetStorage) { - $targetCache->move($sourceInternalPath, $targetInternalPath); - } else { - $targetCache->moveFromCache($sourceStorage->getCache(), $sourceInternalPath, $targetInternalPath); - } + + $time = time(); + + $sourceCache = $sourceStorage->getCache($source); + $sourceUpdater = $sourceStorage->getUpdater(); + $sourcePropagator = $sourceStorage->getPropagator(); + + if ($sourceCache->inCache($source)) { + if ($this->cache->inCache($target)) { + $this->cache->remove($target); } - if (pathinfo($sourceInternalPath, PATHINFO_EXTENSION) !== pathinfo($targetInternalPath, PATHINFO_EXTENSION)) { - // handle mime type change - $mimeType = $targetStorage->getMimeType($targetInternalPath); - $fileId = $targetCache->getId($targetInternalPath); - $targetCache->update($fileId, array('mimetype' => $mimeType)); + if ($sourceStorage === $this->storage) { + $this->cache->move($source, $target); + } else { + $this->cache->moveFromCache($sourceCache, $source, $target); } + } - $targetCache->correctFolderSize($sourceInternalPath); - $targetCache->correctFolderSize($targetInternalPath); - $this->correctParentStorageMtime($sourceStorage, $sourceInternalPath); - $this->correctParentStorageMtime($targetStorage, $targetInternalPath); - $this->updateStorageMTimeOnly($targetStorage, $targetInternalPath); - $this->propagator->addChange($source); - $this->propagator->addChange($target); - $this->propagator->propagateChanges(); + if (pathinfo($source, PATHINFO_EXTENSION) !== pathinfo($target, PATHINFO_EXTENSION)) { + // handle mime type change + $mimeType = $this->storage->getMimeType($target); + $fileId = $this->cache->getId($target); + $this->cache->update($fileId, ['mimetype' => $mimeType]); } + + $sourceCache->correctFolderSize($source); + $this->cache->correctFolderSize($target); + $sourceUpdater->correctParentStorageMtime($source); + $this->correctParentStorageMtime($target); + $this->updateStorageMTimeOnly($target); + $sourcePropagator->propagateChange($source, $time); + $this->propagator->propagateChange($target, $time); } - private function updateStorageMTimeOnly($storage, $internalPath) { - $cache = $storage->getCache(); - $fileId = $cache->getId($internalPath); + private function updateStorageMTimeOnly($internalPath) { + $fileId = $this->cache->getId($internalPath); if ($fileId !== -1) { - $cache->update( + $this->cache->update( $fileId, [ 'mtime' => null, // this magic tells it to not overwrite mtime - 'storage_mtime' => $storage->filemtime($internalPath) + 'storage_mtime' => $this->storage->filemtime($internalPath) ] ); } @@ -217,20 +204,13 @@ class Updater { /** * update the storage_mtime of the direct parent in the cache to the mtime from the storage * - * @param \OC\Files\Storage\Storage $storage * @param string $internalPath */ - private function correctParentStorageMtime($storage, $internalPath) { - $cache = $storage->getCache(); - $parentId = $cache->getParentId($internalPath); + public function correctParentStorageMtime($internalPath) { + $parentId = $this->cache->getParentId($internalPath); $parent = dirname($internalPath); if ($parentId != -1) { - $cache->update($parentId, array('storage_mtime' => $storage->filemtime($parent))); + $this->cache->update($parentId, array('storage_mtime' => $this->storage->filemtime($parent))); } } - - public function __destruct() { - // propagate any leftover changes - $this->propagator->propagateChanges(); - } } diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php index bb810dd45ed..5b5e8697004 100644 --- a/lib/private/files/fileinfo.php +++ b/lib/private/files/fileinfo.php @@ -62,6 +62,11 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { private $owner; /** + * @var string[] + */ + private $childEtags = []; + + /** * @param string|boolean $path * @param Storage\Storage $storage * @param string $internalPath @@ -93,6 +98,8 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { public function offsetGet($offset) { if ($offset === 'type') { return $this->getType(); + } else if ($offset === 'etag') { + return $this->getEtag(); } elseif (isset($this->data[$offset])) { return $this->data[$offset]; } else { @@ -153,7 +160,12 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { * @return string */ public function getEtag() { - return $this->data['etag']; + if (count($this->childEtags) > 0) { + $combinedEtag = $this->data['etag'] . '::' . implode('::', $this->childEtags); + return md5($combinedEtag); + } else { + return $this->data['etag']; + } } /** @@ -285,4 +297,26 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { public function getOwner() { return $this->owner; } + + /** + * Add a cache entry which is the child of this folder + * + * Sets the size, etag and size to for cross-storage childs + * + * @param array $data cache entry for the child + * @param string $entryPath full path of the child entry + */ + public function addSubEntry($data, $entryPath) { + $this->data['size'] += isset($data['size']) ? $data['size'] : 0; + if (isset($data['mtime'])) { + $this->data['mtime'] = max($this->data['mtime'], $data['mtime']); + } + if (isset($data['etag'])) { + // prefix the etag with the relative path of the subentry to propagate etag on mount moves + $relativeEntryPath = substr($entryPath, strlen($this->getPath())); + // attach the permissions to propagate etag on permision changes of submounts + $permissions = isset($data['permissions']) ? $data['permissions'] : 0; + $this->childEtags[] = $relativeEntryPath . '/' . $data['etag'] . $permissions; + } + } } diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index 8e4958a930d..0cd67e343ff 100644 --- a/lib/private/files/storage/common.php +++ b/lib/private/files/storage/common.php @@ -37,7 +37,9 @@ namespace OC\Files\Storage; use OC\Files\Cache\Cache; +use OC\Files\Cache\Propagator; use OC\Files\Cache\Scanner; +use OC\Files\Cache\Updater; use OC\Files\Filesystem; use OC\Files\Cache\Watcher; use OCP\Files\FileNameTooLongException; @@ -64,7 +66,9 @@ abstract class Common implements Storage { protected $cache; protected $scanner; protected $watcher; + protected $propagator; protected $storageCache; + protected $updater; protected $mountOptions = []; @@ -345,6 +349,32 @@ abstract class Common implements Storage { return $this->watcher; } + /** + * get a propagator instance for the cache + * + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher + * @return \OC\Files\Cache\Propagator + */ + public function getPropagator($storage = null) { + if (!$storage) { + $storage = $this; + } + if (!isset($this->propagator)) { + $this->propagator = new Propagator($storage); + } + return $this->propagator; + } + + public function getUpdater($storage = null) { + if (!$storage) { + $storage = $this; + } + if (!isset($this->updater)) { + $this->updater = new Updater($storage); + } + return $this->updater; + } + public function getStorageCache($storage = null) { if (!$storage) { $storage = $this; diff --git a/lib/private/files/storage/dav.php b/lib/private/files/storage/dav.php index dcde7b8029b..9147f572461 100644 --- a/lib/private/files/storage/dav.php +++ b/lib/private/files/storage/dav.php @@ -47,6 +47,7 @@ use OCP\Files\StorageNotAvailableException; use OCP\Util; use Sabre\DAV\Client; use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\Xml\Property\ResourceType; use Sabre\HTTP\ClientException; use Sabre\HTTP\ClientHttpException; @@ -137,7 +138,7 @@ class DAV extends Common { $this->client->setThrowExceptions(true); if ($this->secure === true && $this->certPath) { - $this->client->addTrustedCertificates($this->certPath); + $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath); } } @@ -280,7 +281,8 @@ class DAV extends Common { $response = $this->propfind($path); $responseType = array(); if (isset($response["{DAV:}resourcetype"])) { - $responseType = $response["{DAV:}resourcetype"]->resourceType; + /** @var ResourceType[] $response */ + $responseType = $response["{DAV:}resourcetype"]->getValue(); } return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file'; } catch (ClientHttpException $e) { @@ -554,7 +556,8 @@ class DAV extends Common { $response = $this->propfind($path); $responseType = array(); if (isset($response["{DAV:}resourcetype"])) { - $responseType = $response["{DAV:}resourcetype"]->resourceType; + /** @var ResourceType[] $response */ + $responseType = $response["{DAV:}resourcetype"]->getValue(); } $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file'; if ($type == 'dir') { diff --git a/lib/private/files/storage/storage.php b/lib/private/files/storage/storage.php index f46ac544b56..fb59752ede1 100644 --- a/lib/private/files/storage/storage.php +++ b/lib/private/files/storage/storage.php @@ -68,6 +68,22 @@ interface Storage extends \OCP\Files\Storage { public function getWatcher($path = '', $storage = null); /** + * get a propagator instance for the cache + * + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher + * @return \OC\Files\Cache\Propagator + */ + public function getPropagator($storage = null); + + /** + * get a updater instance for the cache + * + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher + * @return \OC\Files\Cache\Updater + */ + public function getUpdater($storage = null); + + /** * @return \OC\Files\Cache\Storage */ public function getStorageCache(); diff --git a/lib/private/files/storage/wrapper/wrapper.php b/lib/private/files/storage/wrapper/wrapper.php index 048738170db..81284c7aa69 100644 --- a/lib/private/files/storage/wrapper/wrapper.php +++ b/lib/private/files/storage/wrapper/wrapper.php @@ -430,6 +430,20 @@ class Wrapper implements \OC\Files\Storage\Storage { return $this->storage->getWatcher($path, $storage); } + public function getPropagator($storage = null) { + if (!$storage) { + $storage = $this; + } + return $this->storage->getPropagator($storage); + } + + public function getUpdater($storage = null) { + if (!$storage) { + $storage = $this; + } + return $this->storage->getUpdater($storage); + } + /** * @return \OC\Files\Cache\Storage */ diff --git a/lib/private/files/stream/encryption.php b/lib/private/files/stream/encryption.php index 4c328993ef7..585a697c5d1 100644 --- a/lib/private/files/stream/encryption.php +++ b/lib/private/files/stream/encryption.php @@ -167,7 +167,7 @@ class Encryption extends Wrapper { ) )); - return self::wrapSource($source, $mode, $context, 'ocencryption', $wrapper); + return self::wrapSource($source, $context, 'ocencryption', $wrapper, $mode); } /** @@ -181,7 +181,7 @@ class Encryption extends Wrapper { * @return resource * @throws \BadMethodCallException */ - protected static function wrapSource($source, $mode, $context, $protocol, $class) { + protected static function wrapSource($source, $context, $protocol, $class, $mode = 'r+') { try { stream_wrapper_register($protocol, $class); if (@rewinddir($source) === false) { diff --git a/lib/private/files/view.php b/lib/private/files/view.php index cee4b182425..7854790d0e5 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -46,6 +46,7 @@ namespace OC\Files; use Icewind\Streams\CallbackWrapper; use OC\Files\Cache\Updater; use OC\Files\Mount\MoveableMount; +use OC\Files\Storage\Storage; use OC\User\User; use OCP\Files\FileNameTooLongException; use OCP\Files\InvalidCharacterInPathException; @@ -76,9 +77,6 @@ class View { /** @var string */ private $fakeRoot = ''; - /** @var \OC\Files\Cache\Updater */ - protected $updater; - /** * @var \OCP\Lock\ILockingProvider */ @@ -86,6 +84,8 @@ class View { private $lockingEnabled; + private $updaterEnabled = true; + /** * @param string $root * @throws \Exception If $root contains an invalid path @@ -99,7 +99,6 @@ class View { } $this->fakeRoot = $root; - $this->updater = new Updater($this); $this->lockingProvider = \OC::$server->getLockingProvider(); $this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider); } @@ -286,6 +285,35 @@ class View { } } + public function disableCacheUpdate() { + $this->updaterEnabled = false; + } + + public function enableCacheUpdate() { + $this->updaterEnabled = true; + } + + protected function writeUpdate(Storage $storage, $internalPath, $time = null) { + if ($this->updaterEnabled) { + if (is_null($time)) { + $time = time(); + } + $storage->getUpdater()->update($internalPath, $time); + } + } + + protected function removeUpdate(Storage $storage, $internalPath) { + if ($this->updaterEnabled) { + $storage->getUpdater()->remove($internalPath); + } + } + + protected function renameUpdate(Storage $sourceStorage, Storage $targetStorage, $sourceInternalPath, $targetInternalPath) { + if ($this->updaterEnabled) { + $targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); + } + } + /** * @param string $path * @return bool|mixed @@ -569,7 +597,7 @@ class View { fclose($target); fclose($data); - $this->updater->update($path); + $this->writeUpdate($storage, $internalPath); $this->changeLock($path, ILockingProvider::LOCK_SHARED); @@ -703,14 +731,11 @@ class View { if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) { // if it was a rename from a part file to a regular file it was a write and not a rename operation - $this->updater->update($path2); + + $this->writeUpdate($storage2, $internalPath2); } else if ($result) { if ($internalPath1 !== '') { // dont do a cache update for moved mounts - $this->updater->rename($path1, $path2); - } else { // only do etag propagation - $this->getUpdater()->getPropagator()->addChange($path1); - $this->getUpdater()->getPropagator()->addChange($path2); - $this->getUpdater()->getPropagator()->propagateChanges(); + $this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2); } } @@ -807,7 +832,7 @@ class View { $result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2); } - $this->updater->update($path2); + $this->writeUpdate($storage2, $internalPath2); $this->changeLock($path2, ILockingProvider::LOCK_SHARED); $lockTypePath2 = ILockingProvider::LOCK_SHARED; @@ -1017,6 +1042,7 @@ class View { } $run = $this->runHooks($hooks, $path); + /** @var \OC\Files\Storage\Storage $storage */ list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); if ($run and $storage) { if (in_array('write', $hooks) || in_array('delete', $hooks)) { @@ -1038,13 +1064,13 @@ class View { } if (in_array('delete', $hooks) and $result) { - $this->updater->remove($path); + $this->removeUpdate($storage, $internalPath); } if (in_array('write', $hooks) and $operation !== 'fopen') { - $this->updater->update($path); + $this->writeUpdate($storage, $internalPath); } if (in_array('touch', $hooks)) { - $this->updater->update($path, $extraParam); + $this->writeUpdate($storage, $internalPath, $extraParam); } if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) { @@ -1179,6 +1205,48 @@ class View { } /** + * Get file info from cache + * + * If the file is not in cached it will be scanned + * If the file has changed on storage the cache will be updated + * + * @param \OC\Files\Storage\Storage $storage + * @param string $internalPath + * @param string $relativePath + * @return array|bool + */ + private function getCacheEntry($storage, $internalPath, $relativePath) { + $cache = $storage->getCache($internalPath); + $data = $cache->get($internalPath); + $watcher = $storage->getWatcher($internalPath); + + try { + // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data + if (!$data || $data['size'] === -1) { + $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); + if (!$storage->file_exists($internalPath)) { + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); + return false; + } + $scanner = $storage->getScanner($internalPath); + $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); + $data = $cache->get($internalPath); + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); + } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) { + $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); + $watcher->update($internalPath, $data); + $storage->getPropagator()->propagateChange($internalPath, time()); + $data = $cache->get($internalPath); + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); + } + } catch (LockedException $e) { + // if the file is locked we just use the old cache info + } + + return $data; + } + + /** * get the filesystem info * * @param string $path @@ -1189,9 +1257,8 @@ class View { */ public function getFileInfo($path, $includeMountPoints = true) { $this->assertPathLength($path); - $data = array(); if (!Filesystem::isValidPath($path)) { - return $data; + return false; } if (Cache\Scanner::isPartialFile($path)) { return $this->getPartFileInfo($path); @@ -1202,48 +1269,27 @@ class View { $mount = Filesystem::getMountManager()->find($path); $storage = $mount->getStorage(); $internalPath = $mount->getInternalPath($path); - $data = null; if ($storage) { - $cache = $storage->getCache($internalPath); + $data = $this->getCacheEntry($storage, $internalPath, $relativePath); - $data = $cache->get($internalPath); - $watcher = $storage->getWatcher($internalPath); + if (!is_array($data)) { + return false; + } - try { - // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data - if (!$data) { - $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); - if (!$storage->file_exists($internalPath)) { - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); - return false; - } - $scanner = $storage->getScanner($internalPath); - $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); - $data = $cache->get($internalPath); - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); - } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) { - $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); - $watcher->update($internalPath, $data); - $this->updater->propagate($path); - $data = $cache->get($internalPath); - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); - } - } catch (LockedException $e) { - // if the file is locked we just use the old cache info + if ($mount instanceof MoveableMount && $internalPath === '') { + $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE; } + $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath)); + $info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner); + if ($data and isset($data['fileid'])) { - // upgrades from oc6 or lower might not have the permissions set in the file cache - if ($data['permissions'] === 0) { - $data['permissions'] = $storage->getPermissions($data['path']); - $cache->update($data['fileid'], array('permissions' => $data['permissions'])); - } if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') { //add the sizes of other mount points to the folder $extOnly = ($includeMountPoints === 'ext'); - $mountPoints = Filesystem::getMountPoints($path); - foreach ($mountPoints as $mountPoint) { - $subStorage = Filesystem::getStorage($mountPoint); + $mounts = Filesystem::getMountManager()->findIn($path); + foreach ($mounts as $mount) { + $subStorage = $mount->getStorage(); if ($subStorage) { // exclude shared storage ? if ($extOnly && $subStorage instanceof \OC\Files\Storage\Shared) { @@ -1251,22 +1297,16 @@ class View { } $subCache = $subStorage->getCache(''); $rootEntry = $subCache->get(''); - $data['size'] += isset($rootEntry['size']) ? $rootEntry['size'] : 0; + $info->addSubEntry($rootEntry, $mount->getMountPoint()); } } } } - } - if (!$data) { - return false; - } - if ($mount instanceof MoveableMount && $internalPath === '') { - $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE; + return $info; } - $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath)); - return new FileInfo($path, $storage, $internalPath, $data, $mount, $owner); + return false; } /** @@ -1278,9 +1318,8 @@ class View { */ public function getDirectoryContent($directory, $mimetype_filter = '') { $this->assertPathLength($directory); - $result = array(); if (!Filesystem::isValidPath($directory)) { - return $result; + return []; } $path = $this->getAbsolutePath($directory); $path = Filesystem::normalizePath($path); @@ -1291,50 +1330,25 @@ class View { $cache = $storage->getCache($internalPath); $user = \OC_User::getUser(); - /** - * @var \OC\Files\FileInfo[] $files - */ - $files = array(); + $data = $this->getCacheEntry($storage, $internalPath, $directory); - $data = $cache->get($internalPath); - $watcher = $storage->getWatcher($internalPath); - try { - if (!$data or $data['size'] === -1) { - $this->lockFile($directory, ILockingProvider::LOCK_SHARED); - if (!$storage->file_exists($internalPath)) { - $this->unlockFile($directory, ILockingProvider::LOCK_SHARED); - return array(); - } - $scanner = $storage->getScanner($internalPath); - $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); - $data = $cache->get($internalPath); - $this->unlockFile($directory, ILockingProvider::LOCK_SHARED); - } else if ($watcher->needsUpdate($internalPath, $data)) { - $this->lockFile($directory, ILockingProvider::LOCK_SHARED); - $watcher->update($internalPath, $data); - $this->updater->propagate($path); - $data = $cache->get($internalPath); - $this->unlockFile($directory, ILockingProvider::LOCK_SHARED); - } - } catch (LockedException $e) { - // if the file is locked we just use the old cache info + if (!is_array($data) || !isset($data['fileid'])) { + return []; } $folderId = $data['fileid']; $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter - foreach ($contents as $content) { - if ($content['permissions'] === 0) { - $content['permissions'] = $storage->getPermissions($content['path']); - $cache->update($content['fileid'], array('permissions' => $content['permissions'])); - } - // if sharing was disabled for the user we remove the share permissions + /** + * @var \OC\Files\FileInfo[] $files + */ + $files = array_map(function (array $content) use ($path, $storage, $mount) { if (\OCP\Util::isSharingDisabledForUser()) { $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; } $owner = $this->getUserObjectForOwner($storage->getOwner($content['path'])); - $files[] = new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner); - } + return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner); + }, $contents); //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders $mounts = Filesystem::getMountManager()->findIn($path); @@ -1345,7 +1359,8 @@ class View { if ($subStorage) { $subCache = $subStorage->getCache(''); - if ($subCache->getStatus('') === Cache\Cache::NOT_FOUND) { + $rootEntry = $subCache->get(''); + if (!$rootEntry) { $subScanner = $subStorage->getScanner(''); try { $subScanner->scanFile(''); @@ -1363,17 +1378,17 @@ class View { ); continue; } + $rootEntry = $subCache->get(''); } - $rootEntry = $subCache->get(''); if ($rootEntry) { $relativePath = trim(substr($mountPoint, $dirLength), '/'); if ($pos = strpos($relativePath, '/')) { //mountpoint inside subfolder add size to the correct folder $entryName = substr($relativePath, 0, $pos); foreach ($files as &$entry) { - if ($entry['name'] === $entryName) { - $entry['size'] += $rootEntry['size']; + if ($entry->getName() === $entryName) { + $entry->addSubEntry($rootEntry, $mountPoint); } } } else { //mountpoint in this folder, add an entry for it @@ -1410,23 +1425,23 @@ class View { } if ($mimetype_filter) { - foreach ($files as $file) { + $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) { if (strpos($mimetype_filter, '/')) { - if ($file['mimetype'] === $mimetype_filter) { + if ($file->getMimetype() === $mimetype_filter) { $result[] = $file; } } else { - if ($file['mimepart'] === $mimetype_filter) { + if ($file->getMimePart() === $mimetype_filter) { $result[] = $file; } } - } - } else { - $result = $files; + }); } - } - return $result; + return $files; + } else { + return []; + } } /** @@ -1707,13 +1722,6 @@ class View { } /** - * @return Updater - */ - public function getUpdater() { - return $this->updater; - } - - /** * @param string $path * @param string $fileName * @throws InvalidPathException |