aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobin Appelman <icewind@owncloud.com>2015-11-10 16:14:08 +0100
committerRobin Appelman <icewind@owncloud.com>2015-11-19 13:32:00 +0100
commit888df3933df7f3588de11085035d2d3ae9292fb0 (patch)
treeb7976666e9249b0be163b57fb064f3a1ade26919
parentd006a7c723e4c51a4132b0f8817392bf1f3cd534 (diff)
downloadnextcloud-server-888df3933df7f3588de11085035d2d3ae9292fb0.tar.gz
nextcloud-server-888df3933df7f3588de11085035d2d3ae9292fb0.zip
take the etag of child mounts into account for the folder etag
this replaces shared etag propagation
-rw-r--r--apps/files_sharing/appinfo/app.php1
-rw-r--r--apps/files_sharing/appinfo/application.php31
-rw-r--r--apps/files_sharing/lib/mountprovider.php16
-rw-r--r--apps/files_sharing/lib/propagation/changewatcher.php110
-rw-r--r--apps/files_sharing/lib/propagation/grouppropagationmanager.php133
-rw-r--r--apps/files_sharing/lib/propagation/propagationmanager.php144
-rw-r--r--apps/files_sharing/lib/propagation/recipientpropagator.php164
-rw-r--r--apps/files_sharing/lib/sharedmount.php14
-rw-r--r--apps/files_sharing/lib/sharedpropagator.php43
-rw-r--r--apps/files_sharing/lib/sharedstorage.php16
-rw-r--r--apps/files_sharing/tests/grouppropagationmanager.php173
-rw-r--r--apps/files_sharing/tests/testcase.php1
-rw-r--r--lib/private/files/cache/cache.php16
-rw-r--r--lib/private/files/cache/changepropagator.php24
-rw-r--r--lib/private/files/cache/propagator.php66
-rw-r--r--lib/private/files/fileinfo.php27
-rw-r--r--lib/private/files/storage/common.php18
-rw-r--r--lib/private/files/storage/storage.php8
-rw-r--r--lib/private/files/storage/wrapper/wrapper.php7
-rw-r--r--lib/private/files/view.php13
-rw-r--r--tests/lib/files/cache/changepropagator.php33
21 files changed, 243 insertions, 815 deletions
diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php
index 03e448be0af..5f56340d254 100644
--- a/apps/files_sharing/appinfo/app.php
+++ b/apps/files_sharing/appinfo/app.php
@@ -42,7 +42,6 @@ $l = \OC::$server->getL10N('files_sharing');
$application = new Application();
$application->registerMountProviders();
-$application->setupPropagation();
\OCP\App::registerAdmin('files_sharing', 'settings-admin');
\OCP\App::registerPersonal('files_sharing', 'settings-personal');
diff --git a/apps/files_sharing/appinfo/application.php b/apps/files_sharing/appinfo/application.php
index 545a9425083..ffe3a6a513f 100644
--- a/apps/files_sharing/appinfo/application.php
+++ b/apps/files_sharing/appinfo/application.php
@@ -27,8 +27,6 @@ namespace OCA\Files_Sharing\AppInfo;
use OCA\Files_Sharing\Helper;
use OCA\Files_Sharing\MountProvider;
-use OCA\Files_Sharing\Propagation\PropagationManager;
-use OCA\Files_Sharing\Propagation\GroupPropagationManager;
use OCP\AppFramework\App;
use OC\AppFramework\Utility\SimpleContainer;
use OCA\Files_Sharing\Controllers\ExternalSharesController;
@@ -116,8 +114,7 @@ class Application extends App {
/** @var \OCP\IServerContainer $server */
$server = $c->query('ServerContainer');
return new MountProvider(
- $server->getConfig(),
- $c->query('PropagationManager')
+ $server->getConfig()
);
});
@@ -132,25 +129,6 @@ class Application extends App {
);
});
- $container->registerService('PropagationManager', function (IContainer $c) {
- /** @var \OCP\IServerContainer $server */
- $server = $c->query('ServerContainer');
- return new PropagationManager(
- $server->getUserSession(),
- $server->getConfig()
- );
- });
-
- $container->registerService('GroupPropagationManager', function (IContainer $c) {
- /** @var \OCP\IServerContainer $server */
- $server = $c->query('ServerContainer');
- return new GroupPropagationManager(
- $server->getUserSession(),
- $server->getGroupManager(),
- $c->query('PropagationManager')
- );
- });
-
/*
* Register capabilities
*/
@@ -164,11 +142,4 @@ class Application extends App {
$mountProviderCollection->registerProvider($this->getContainer()->query('MountProvider'));
$mountProviderCollection->registerProvider($this->getContainer()->query('ExternalMountProvider'));
}
-
- public function setupPropagation() {
- $propagationManager = $this->getContainer()->query('PropagationManager');
- \OCP\Util::connectHook('OC_Filesystem', 'setup', $propagationManager, 'globalSetup');
-
- $this->getContainer()->query('GroupPropagationManager')->globalSetup();
- }
}
diff --git a/apps/files_sharing/lib/mountprovider.php b/apps/files_sharing/lib/mountprovider.php
index 458e7f2619b..74a2a3ff4d6 100644
--- a/apps/files_sharing/lib/mountprovider.php
+++ b/apps/files_sharing/lib/mountprovider.php
@@ -24,7 +24,6 @@ namespace OCA\Files_Sharing;
use OC\Files\Filesystem;
use OC\User\NoUserException;
-use OCA\Files_Sharing\Propagation\PropagationManager;
use OCP\Files\Config\IMountProvider;
use OCP\Files\Storage\IStorageFactory;
use OCP\IConfig;
@@ -37,17 +36,10 @@ class MountProvider implements IMountProvider {
protected $config;
/**
- * @var \OCA\Files_Sharing\Propagation\PropagationManager
- */
- protected $propagationManager;
-
- /**
* @param \OCP\IConfig $config
- * @param \OCA\Files_Sharing\Propagation\PropagationManager $propagationManager
*/
- public function __construct(IConfig $config, PropagationManager $propagationManager) {
+ public function __construct(IConfig $config) {
$this->config = $config;
- $this->propagationManager = $propagationManager;
}
@@ -60,21 +52,15 @@ class MountProvider implements IMountProvider {
*/
public function getMountsForUser(IUser $user, IStorageFactory $storageFactory) {
$shares = \OCP\Share::getItemsSharedWithUser('file', $user->getUID());
- $propagator = $this->propagationManager->getSharePropagator($user->getUID());
- $propagator->propagateDirtyMountPoints($shares);
$shares = array_filter($shares, function ($share) {
return $share['permissions'] > 0;
});
$shares = array_map(function ($share) use ($user, $storageFactory) {
- // for updating etags for the share owner when we make changes to this share.
- $ownerPropagator = $this->propagationManager->getChangePropagator($share['uid_owner']);
return new SharedMount(
'\OC\Files\Storage\Shared',
'/' . $user->getUID() . '/' . $share['file_target'],
array(
- 'propagationManager' => $this->propagationManager,
- 'propagator' => $ownerPropagator,
'share' => $share,
'user' => $user->getUID()
),
diff --git a/apps/files_sharing/lib/propagation/changewatcher.php b/apps/files_sharing/lib/propagation/changewatcher.php
deleted file mode 100644
index e61c161da19..00000000000
--- a/apps/files_sharing/lib/propagation/changewatcher.php
+++ /dev/null
@@ -1,110 +0,0 @@
-<?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 OCA\Files_Sharing\Propagation;
-
-use OC\Files\Cache\ChangePropagator;
-use OC\Files\Filesystem;
-use OC\Files\View;
-use OCA\Files_Sharing\SharedMount;
-
-/**
- * Watch for changes made in a shared mount and propagate the changes to the share owner
- */
-class ChangeWatcher {
- /**
- * The user view for the logged in user
- *
- * @var \OC\Files\View
- */
- private $baseView;
-
- /**
- * @var RecipientPropagator
- */
- private $recipientPropagator;
-
- /**
- * @param \OC\Files\View $baseView the view for the logged in user
- * @param RecipientPropagator $recipientPropagator
- */
- public function __construct(View $baseView, RecipientPropagator $recipientPropagator) {
- $this->baseView = $baseView;
- $this->recipientPropagator = $recipientPropagator;
- }
-
-
- public function writeHook($params) {
- $path = $params['path'];
- $fullPath = $this->baseView->getAbsolutePath($path);
- $mount = $this->baseView->getMount($path);
- if ($mount instanceof SharedMount) {
- $this->propagateForOwner($mount->getShare(), $mount->getInternalPath($fullPath), $mount->getOwnerPropagator());
- }
- $info = $this->baseView->getFileInfo($path);
- if ($info) {
- // trigger propagation if the subject of the write hook is shared.
- // if a parent folder of $path is shared the propagation will be triggered from the change propagator hooks
- $this->recipientPropagator->propagateById($info->getId());
- }
- }
-
- public function renameHook($params) {
- $path1 = $params['oldpath'];
- $path2 = $params['newpath'];
- $fullPath1 = $this->baseView->getAbsolutePath($path1);
- $fullPath2 = $this->baseView->getAbsolutePath($path2);
- $mount1 = $this->baseView->getMount($path1);
- $mount2 = $this->baseView->getMount($path2);
- if ($mount1 instanceof SharedMount and $mount1->getInternalPath($fullPath1) !== '') {
- $this->propagateForOwner($mount1->getShare(), $mount1->getInternalPath($fullPath1), $mount1->getOwnerPropagator());
- }
- if ($mount1 !== $mount2 and $mount2 instanceof SharedMount and $mount2->getInternalPath($fullPath2) !== '') {
- $this->propagateForOwner($mount2->getShare(), $mount2->getInternalPath($fullPath2), $mount2->getOwnerPropagator());
- }
- }
-
- /**
- * @param array $share
- * @param string $internalPath
- * @param \OC\Files\Cache\ChangePropagator $propagator
- */
- private function propagateForOwner($share, $internalPath, ChangePropagator $propagator) {
- // note that we have already set up the filesystem for the owner when mounting the share
- $view = new View('/' . $share['uid_owner'] . '/files');
-
- $shareRootPath = $view->getPath($share['item_source']);
- if (!is_null($shareRootPath)) {
- $path = $shareRootPath . '/' . $internalPath;
- $path = Filesystem::normalizePath($path);
- $propagator->addChange($path);
- $propagator->propagateChanges();
- }
- }
-
- public function permissionsHook($params) {
- $share = $params['share'];
-
- if ($share['item_type'] === 'file' || $share['item_type'] === 'folder') {
- $this->recipientPropagator->markDirty($share, microtime(true));
- }
- }
-}
diff --git a/apps/files_sharing/lib/propagation/grouppropagationmanager.php b/apps/files_sharing/lib/propagation/grouppropagationmanager.php
deleted file mode 100644
index ba550dccec3..00000000000
--- a/apps/files_sharing/lib/propagation/grouppropagationmanager.php
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-/**
- * @author Vincent Petry <pvince81@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 OCA\Files_Sharing\Propagation;
-
-use OC\Files\Filesystem;
-use OC\Files\View;
-use OCP\IConfig;
-use OCP\IUserSession;
-use OCP\IGroup;
-use OCP\IUser;
-use OCP\IGroupManager;
-use OCA\Files_Sharing\Propagation\PropagationManager;
-
-/**
- * Propagate changes on group changes
- */
-class GroupPropagationManager {
- /**
- * @var \OCP\IUserSession
- */
- private $userSession;
-
- /**
- * @var \OCP\IGroupManager
- */
- private $groupManager;
-
- /**
- * @var PropagationManager
- */
- private $propagationManager;
-
- /**
- * Items shared with a given user.
- * Key is user id and value is an array of shares.
- *
- * @var array
- */
- private $userShares = [];
-
- public function __construct(IUserSession $userSession, IGroupManager $groupManager, PropagationManager $propagationManager) {
- $this->userSession = $userSession;
- $this->groupManager = $groupManager;
- $this->propagationManager = $propagationManager;
- }
-
- public function onPreProcessUser(IGroup $group, IUser $targetUser) {
- $this->userShares[$targetUser->getUID()] = $this->getUserShares($targetUser->getUID());
- }
-
- public function onPostAddUser(IGroup $group, IUser $targetUser) {
- $targetUserId = $targetUser->getUID();
- $sharesAfter = $this->getUserShares($targetUserId);
-
- $this->propagateSharesDiff($targetUserId, $sharesAfter, $this->userShares[$targetUserId]);
- unset($this->userShares[$targetUserId]);
- }
-
- public function onPostRemoveUser(IGroup $group, IUser $targetUser) {
- $targetUserId = $targetUser->getUID();
- $sharesAfter = $this->getUserShares($targetUserId);
-
- $this->propagateSharesDiff($targetUserId, $this->userShares[$targetUserId], $sharesAfter);
- unset($this->userShares[$targetUserId]);
- }
-
- private function getUserShares($targetUserId) {
- return \OCP\Share::getItemsSharedWithUser('file', $targetUserId);
- }
-
- /**
- * Propagate etag for the shares that are in $shares1 but not in $shares2.
- *
- * @param string $targetUserId user id for which to propagate shares
- * @param array $shares1
- * @param array $shares2
- */
- private function propagateSharesDiff($targetUserId, $shares1, $shares2) {
- $sharesToPropagate = array_udiff(
- $shares1,
- $shares2,
- function($share1, $share2) {
- return ($share2['id'] - $share1['id']);
- }
- );
-
- \OC\Files\Filesystem::initMountPoints($targetUserId);
- $this->propagationManager->propagateSharesToUser($sharesToPropagate, $targetUserId);
- }
-
- /**
- * To be called from setupFS trough a hook
- *
- * Sets up listening to changes made to shares owned by the current user
- */
- public function globalSetup() {
- $user = $this->userSession->getUser();
- if (!$user) {
- return;
- }
-
- $this->groupManager->listen('\OC\Group', 'preAddUser', [$this, 'onPreProcessUser']);
- $this->groupManager->listen('\OC\Group', 'postAddUser', [$this, 'onPostAddUser']);
- $this->groupManager->listen('\OC\Group', 'preRemoveUser', [$this, 'onPreProcessUser']);
- $this->groupManager->listen('\OC\Group', 'postRemoveUser', [$this, 'onPostRemoveUser']);
- }
-
- public function tearDown() {
- $this->groupManager->removeListener('\OC\Group', 'preAddUser', [$this, 'onPreProcessUser']);
- $this->groupManager->removeListener('\OC\Group', 'postAddUser', [$this, 'onPostAddUser']);
- $this->groupManager->removeListener('\OC\Group', 'preRemoveUser', [$this, 'onPreProcessUser']);
- $this->groupManager->removeListener('\OC\Group', 'postRemoveUser', [$this, 'onPostRemoveUser']);
- }
-}
diff --git a/apps/files_sharing/lib/propagation/propagationmanager.php b/apps/files_sharing/lib/propagation/propagationmanager.php
deleted file mode 100644
index aac9428240d..00000000000
--- a/apps/files_sharing/lib/propagation/propagationmanager.php
+++ /dev/null
@@ -1,144 +0,0 @@
-<?php
-/**
- * @author Robin Appelman <icewind@owncloud.com>
- * @author Vincent Petry <pvince81@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 OCA\Files_Sharing\Propagation;
-
-use OC\Files\Filesystem;
-use OC\Files\View;
-use OCP\IConfig;
-use OCP\IUserSession;
-use OCP\Util;
-
-
-/**
- * Keep track of all change and share propagators by owner
- */
-class PropagationManager {
- /**
- * @var \OCP\IUserSession
- */
- private $userSession;
-
- /**
- * @var \OCP\IConfig
- */
- private $config;
-
- /**
- * Change propagators for share owner
- *
- * @var \OC\Files\Cache\ChangePropagator[]
- */
- private $changePropagators = [];
-
- /**
- * Recipient propagators
- *
- * @var \OCA\Files_Sharing\Propagation\RecipientPropagator[]
- */
- private $sharePropagators = [];
-
- public function __construct(IUserSession $userSession, IConfig $config) {
- $this->userSession = $userSession;
- $this->config = $config;
- }
-
- /**
- * @param string $user
- * @return \OC\Files\Cache\ChangePropagator
- */
- public function getChangePropagator($user) {
- $activeUser = $this->userSession->getUser();
-
- // for the local user we want to propagator from the active view, not any cached one
- if ($activeUser && $activeUser->getUID() === $user && Filesystem::getView() instanceof View) {
- // it's important that we take the existing propagator here to make sure we can listen to external changes
- $this->changePropagators[$user] = Filesystem::getView()->getUpdater()->getPropagator();
- }
- if (isset($this->changePropagators[$user])) {
- return $this->changePropagators[$user];
- }
- $view = new View('/' . $user . '/files');
- $this->changePropagators[$user] = $view->getUpdater()->getPropagator();
- return $this->changePropagators[$user];
- }
-
- /**
- * Propagates etag changes for the given shares to the given user
- *
- * @param array array of shares for which to trigger etag change
- * @param string $user
- */
- public function propagateSharesToUser($shares, $user) {
- $changePropagator = $this->getChangePropagator($user);
- foreach ($shares as $share) {
- $changePropagator->addChange($share['file_target']);
- }
- $time = microtime(true);
- $changePropagator->propagateChanges(floor($time));
- }
-
- /**
- * @param string $user
- * @return \OCA\Files_Sharing\Propagation\RecipientPropagator
- */
- public function getSharePropagator($user) {
- if (isset($this->sharePropagators[$user])) {
- return $this->sharePropagators[$user];
- }
- $this->sharePropagators[$user] = new RecipientPropagator($user, $this->getChangePropagator($user), $this->config, $this);
- return $this->sharePropagators[$user];
- }
-
- /**
- * Attach the recipient propagator for $user to the change propagator of a share owner to mark shares as dirty when the owner makes a change to a share
- *
- * @param string $shareOwner
- * @param string $user
- */
- public function listenToOwnerChanges($shareOwner, $user) {
- $sharePropagator = $this->getSharePropagator($user);
- $ownerPropagator = $this->getChangePropagator($shareOwner);
- $sharePropagator->attachToPropagator($ownerPropagator, $shareOwner);
- }
-
- /**
- * To be called from setupFS trough a hook
- *
- * Sets up listening to changes made to shares owned by the current user
- */
- public function globalSetup() {
- $user = $this->userSession->getUser();
- if (!$user) {
- return;
- }
- $recipientPropagator = $this->getSharePropagator($user->getUID());
- $watcher = new ChangeWatcher(Filesystem::getView(), $recipientPropagator);
-
- // for marking shares owned by the active user as dirty when a file inside them changes
- $this->listenToOwnerChanges($user->getUID(), $user->getUID());
- Util::connectHook('OC_Filesystem', 'post_write', $watcher, 'writeHook');
- Util::connectHook('OC_Filesystem', 'post_delete', $watcher, 'writeHook');
- Util::connectHook('OC_Filesystem', 'post_rename', $watcher, 'renameHook');
- Util::connectHook('OCP\Share', 'post_update_permissions', $watcher, 'permissionsHook');
- }
-}
diff --git a/apps/files_sharing/lib/propagation/recipientpropagator.php b/apps/files_sharing/lib/propagation/recipientpropagator.php
deleted file mode 100644
index 5eacf4c0f6e..00000000000
--- a/apps/files_sharing/lib/propagation/recipientpropagator.php
+++ /dev/null
@@ -1,164 +0,0 @@
-<?php
-/**
- * @author Lukas Reschke <lukas@owncloud.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @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 OCA\Files_Sharing\Propagation;
-
-use OC\Files\Cache\ChangePropagator;
-use OC\Files\View;
-use OC\Share\Share;
-use OCP\Files\NotFoundException;
-
-/**
- * Propagate etags for share recipients
- */
-class RecipientPropagator {
- /**
- * @var string
- */
- protected $userId;
-
- /**
- * @var \OC\Files\Cache\ChangePropagator
- */
- protected $changePropagator;
-
- /**
- * @var \OCP\IConfig
- */
- protected $config;
-
- /**
- * @var PropagationManager
- */
- private $manager;
-
- /**
- * @param string $userId current user, must match the propagator's
- * user
- * @param \OC\Files\Cache\ChangePropagator $changePropagator change propagator
- * initialized with a view for $user
- * @param \OCP\IConfig $config
- * @param PropagationManager $manager
- */
- public function __construct($userId, $changePropagator, $config, PropagationManager $manager) {
- $this->userId = $userId;
- $this->changePropagator = $changePropagator;
- $this->config = $config;
- $this->manager = $manager;
- }
-
- /**
- * Propagate the etag changes for all shares marked as dirty and mark the shares as clean
- *
- * @param array $shares the shares for the users
- * @param int $time
- */
- public function propagateDirtyMountPoints(array $shares, $time = null) {
- if ($time === null) {
- $time = microtime(true);
- }
- $dirtyShares = $this->getDirtyShares($shares);
- foreach ($dirtyShares as $share) {
- $this->changePropagator->addChange($share['file_target']);
- }
- if (count($dirtyShares)) {
- $this->config->setUserValue($this->userId, 'files_sharing', 'last_propagate', $time);
- $this->changePropagator->propagateChanges(floor($time));
- }
- }
-
- /**
- * Get all shares we need to update the etag for
- *
- * @param array $shares the shares for the users
- * @return string[]
- */
- protected function getDirtyShares($shares) {
- $dirty = [];
- $userTime = $this->config->getUserValue($this->userId, 'files_sharing', 'last_propagate', 0);
- foreach ($shares as $share) {
- $updateTime = $this->config->getAppValue('files_sharing', $share['id'], 0);
- if ($updateTime >= $userTime) {
- $dirty[] = $share;
- }
- }
- return $dirty;
- }
-
- /**
- * @param array $share
- * @param float $time
- */
- public function markDirty($share, $time = null) {
- if ($time === null) {
- $time = microtime(true);
- }
- $this->config->setAppValue('files_sharing', $share['id'], $time);
- }
-
- /**
- * Listen on the propagator for updates made to shares owned by a user
- *
- * @param \OC\Files\Cache\ChangePropagator $propagator
- * @param string $owner
- */
- public function attachToPropagator(ChangePropagator $propagator, $owner) {
- $propagator->listen('\OC\Files', 'propagate', function ($path, $entry) use ($owner) {
- $this->propagateById($entry['fileid']);
- });
- }
-
- protected $propagatingIds = [];
-
- /**
- * @param int $id
- */
- public function propagateById($id) {
- if (isset($this->propagatingIds[$id])) {
- return;
- }
- $this->propagatingIds[$id] = true;
- $shares = Share::getAllSharesForFileId($id);
- foreach ($shares as $share) {
- // propagate down the share tree
- $this->markDirty($share, microtime(true));
-
- // propagate up the share tree
- if ($share['share_with'] === $this->userId) {
- $user = $share['uid_owner'];
- $view = new View('/' . $user . '/files');
-
- try {
- $path = $view->getPath($share['file_source']);
- } catch (NotFoundException $e) {
- $path = null;
- }
-
- $watcher = new ChangeWatcher($view, $this->manager->getSharePropagator($user));
- $watcher->writeHook(['path' => $path]);
- }
- }
-
- unset($this->propagatingIds[$id]);
- }
-}
diff --git a/apps/files_sharing/lib/sharedmount.php b/apps/files_sharing/lib/sharedmount.php
index a1387957867..275fea97c7f 100644
--- a/apps/files_sharing/lib/sharedmount.php
+++ b/apps/files_sharing/lib/sharedmount.php
@@ -39,11 +39,6 @@ class SharedMount extends MountPoint implements MoveableMount {
protected $storage = null;
/**
- * @var \OC\Files\Cache\ChangePropagator
- */
- protected $ownerPropagator;
-
- /**
* @var \OC\Files\View
*/
private $recipientView;
@@ -54,8 +49,6 @@ class SharedMount extends MountPoint implements MoveableMount {
private $user;
public function __construct($storage, $mountpoint, $arguments = null, $loader = null) {
- // first update the mount point before creating the parent
- $this->ownerPropagator = $arguments['propagator'];
$this->user = $arguments['user'];
$this->recipientView = new View('/' . $this->user . '/files');
$newMountPoint = $this->verifyMountPoint($arguments['share']);
@@ -201,11 +194,4 @@ class SharedMount extends MountPoint implements MoveableMount {
public function getShare() {
return $this->getStorage()->getShare();
}
-
- /**
- * @return \OC\Files\Cache\ChangePropagator
- */
- public function getOwnerPropagator() {
- return $this->ownerPropagator;
- }
}
diff --git a/apps/files_sharing/lib/sharedpropagator.php b/apps/files_sharing/lib/sharedpropagator.php
new file mode 100644
index 00000000000..fcb4b92dd33
--- /dev/null
+++ b/apps/files_sharing/lib/sharedpropagator.php
@@ -0,0 +1,43 @@
+<?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 OCA\Files_Sharing;
+
+use OC\Files\Cache\Propagator;
+
+class SharedPropagator extends Propagator {
+ /**
+ * @var \OC\Files\Storage\Shared
+ */
+ protected $storage;
+
+ /**
+ * @param string $internalPath
+ * @param int $time
+ * @return array[] all propagated entries
+ */
+ public function propagateChange($internalPath, $time) {
+ $source = $this->storage->getSourcePath($internalPath);
+ /** @var \OC\Files\Storage\Storage $storage */
+ list($storage, $sourceInternalPath) = \OC\Files\Filesystem::resolvePath($source);
+ return $storage->getPropagator()->propagateChange($sourceInternalPath, $time);
+ }
+}
diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php
index 18e02844179..4807b5ee738 100644
--- a/apps/files_sharing/lib/sharedstorage.php
+++ b/apps/files_sharing/lib/sharedstorage.php
@@ -51,11 +51,6 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
private $ownerView;
/**
- * @var \OCA\Files_Sharing\Propagation\PropagationManager
- */
- private $propagationManager;
-
- /**
* @var string
*/
private $user;
@@ -65,7 +60,6 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
public function __construct($arguments) {
$this->share = $arguments['share'];
$this->ownerView = $arguments['ownerView'];
- $this->propagationManager = $arguments['propagationManager'];
$this->user = $arguments['user'];
}
@@ -75,9 +69,6 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
}
$this->initialized = true;
Filesystem::initMountPoints($this->share['uid_owner']);
-
- // for updating our etags when changes are made to the share from the owners side (probably indirectly by us trough another share)
- $this->propagationManager->listenToOwnerChanges($this->share['uid_owner'], $this->user);
}
/**
@@ -571,6 +562,13 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
return new \OC\Files\Cache\Shared_Watcher($storage);
}
+ public function getPropagator($storage = null) {
+ if (!$storage) {
+ $storage = $this;
+ }
+ return new \OCA\Files_Sharing\SharedPropagator($storage);
+ }
+
public function getOwner($path) {
if ($path == '') {
$path = $this->getMountPoint();
diff --git a/apps/files_sharing/tests/grouppropagationmanager.php b/apps/files_sharing/tests/grouppropagationmanager.php
deleted file mode 100644
index ea32ca4f7ec..00000000000
--- a/apps/files_sharing/tests/grouppropagationmanager.php
+++ /dev/null
@@ -1,173 +0,0 @@
-<?php
-/**
- * @author Vincent Petry <pvince81@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 OCA\Files_sharing\Tests;
-
-use OC\Files\View;
-use OCP\IGroupManager;
-use OCP\IGroup;
-use OCP\IUser;
-use OCP\Share;
-use OCA\Files_Sharing\Propagation\GroupPropagationManager;
-use OCA\Files_Sharing\Propagation\PropagationManager;
-
-class GroupPropagationManagerTest extends TestCase {
-
- /**
- * @var GroupPropagationManager
- */
- private $groupPropagationManager;
-
- /**
- * @var IGroupManager
- */
- private $groupManager;
-
- /**
- * @var PropagationManager
- */
- private $propagationManager;
-
- /**
- * @var IGroup
- */
- private $recipientGroup;
-
- /**
- * @var IUser
- */
- private $recipientUser;
-
- /**
- * @var array
- */
- private $fileInfo;
-
- protected function setUp() {
- parent::setUp();
-
- $user = $this->getMockBuilder('\OCP\IUser')
- ->disableOriginalConstructor()
- ->getMock();
- $user->method('getUID')->willReturn(self::TEST_FILES_SHARING_API_USER1);
- $userSession = $this->getMockBuilder('\OCP\IUserSession')
- ->disableOriginalConstructor()
- ->getMock();
- $userSession->method('getUser')->willReturn(selF::TEST_FILES_SHARING_API_USER1);
-
- $this->propagationManager = $this->getMockBuilder('OCA\Files_Sharing\Propagation\PropagationManager')
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->groupManager = \OC::$server->getGroupManager();
- $this->groupPropagationManager = new GroupPropagationManager(
- $userSession,
- $this->groupManager,
- $this->propagationManager
- );
- $this->groupPropagationManager->globalSetup();
-
- // since the sharing code is not mockable, we have to create a real folder
- $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1);
- $view1 = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files');
- $view1->mkdir('/folder');
-
- $this->fileInfo = $view1->getFileInfo('/folder');
-
- $this->recipientGroup = $this->groupManager->get(self::TEST_FILES_SHARING_API_GROUP1);
- $this->recipientUser = \OC::$server->getUserManager()->get(self::TEST_FILES_SHARING_API_USER3);
-
- Share::shareItem(
- 'folder',
- $this->fileInfo['fileid'],
- Share::SHARE_TYPE_GROUP,
- $this->recipientGroup->getGID(),
- \OCP\Constants::PERMISSION_READ
- );
-
- $this->loginAsUser($this->recipientUser->getUID());
- }
-
- protected function tearDown() {
- $this->groupPropagationManager->tearDown();
- $this->recipientGroup->removeUser($this->recipientUser);
- parent::tearDown();
- }
-
- public function testPropagateWhenAddedToGroup() {
- $this->propagationManager->expects($this->once())
- ->method('propagateSharesToUser')
- ->with($this->callback(function($shares) {
- if (count($shares) !== 1) {
- return false;
- }
- $share = array_values($shares)[0];
- return $share['file_source'] === $this->fileInfo['fileid'] &&
- $share['share_with'] === $this->recipientGroup->getGID() &&
- $share['file_target'] === '/folder';
- }), $this->recipientUser->getUID());
-
- $this->recipientGroup->addUser($this->recipientUser);
- }
-
- public function testPropagateWhenRemovedFromGroup() {
- $this->recipientGroup->addUser($this->recipientUser);
-
- $this->propagationManager->expects($this->once())
- ->method('propagateSharesToUser')
- ->with($this->callback(function($shares) {
- if (count($shares) !== 1) {
- return false;
- }
- $share = array_values($shares)[0];
- return $share['file_source'] === $this->fileInfo['fileid'] &&
- $share['share_with'] === $this->recipientGroup->getGID() &&
- $share['file_target'] === '/folder';
- }), $this->recipientUser->getUID());
-
- $this->recipientGroup->removeUser($this->recipientUser);
- }
-
- public function testPropagateWhenRemovedFromGroupWithSubdirTarget() {
- $this->recipientGroup->addUser($this->recipientUser);
-
- // relogin to refresh mount points
- $this->loginAsUser($this->recipientUser->getUID());
- $recipientView = new View('/' . $this->recipientUser->getUID() . '/files');
-
- $this->assertTrue($recipientView->mkdir('sub'));
- $this->assertTrue($recipientView->rename('folder', 'sub/folder'));
-
- $this->propagationManager->expects($this->once())
- ->method('propagateSharesToUser')
- ->with($this->callback(function($shares) {
- if (count($shares) !== 1) {
- return false;
- }
- $share = array_values($shares)[0];
- return $share['file_source'] === $this->fileInfo['fileid'] &&
- $share['share_with'] === $this->recipientGroup->getGID() &&
- $share['file_target'] === '/sub/folder';
- }), $this->recipientUser->getUID());
-
- $this->recipientGroup->removeUser($this->recipientUser);
- }
-}
diff --git a/apps/files_sharing/tests/testcase.php b/apps/files_sharing/tests/testcase.php
index c91734a5b03..6a72a34149a 100644
--- a/apps/files_sharing/tests/testcase.php
+++ b/apps/files_sharing/tests/testcase.php
@@ -61,7 +61,6 @@ abstract class TestCase extends \Test\TestCase {
$application = new Application();
$application->registerMountProviders();
- $application->setupPropagation();
// reset backend
\OC_User::clearBackends();
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/fileinfo.php b/lib/private/files/fileinfo.php
index 2d2ef325d38..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'];
+ }
}
/**
@@ -292,8 +304,19 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
* 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) {
+ 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 77a70226b37..a52e7c35f87 100644
--- a/lib/private/files/storage/common.php
+++ b/lib/private/files/storage/common.php
@@ -37,6 +37,7 @@
namespace OC\Files\Storage;
use OC\Files\Cache\Cache;
+use OC\Files\Cache\Propagator;
use OC\Files\Cache\Scanner;
use OC\Files\Filesystem;
use OC\Files\Cache\Watcher;
@@ -64,6 +65,7 @@ abstract class Common implements Storage {
protected $cache;
protected $scanner;
protected $watcher;
+ protected $propagator;
protected $storageCache;
protected $mountOptions = [];
@@ -345,6 +347,22 @@ 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 getStorageCache($storage = null) {
if (!$storage) {
$storage = $this;
diff --git a/lib/private/files/storage/storage.php b/lib/private/files/storage/storage.php
index f46ac544b56..3399c717890 100644
--- a/lib/private/files/storage/storage.php
+++ b/lib/private/files/storage/storage.php
@@ -68,6 +68,14 @@ 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);
+
+ /**
* @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..5327b94211b 100644
--- a/lib/private/files/storage/wrapper/wrapper.php
+++ b/lib/private/files/storage/wrapper/wrapper.php
@@ -430,6 +430,13 @@ 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);
+ }
+
/**
* @return \OC\Files\Cache\Storage
*/
diff --git a/lib/private/files/view.php b/lib/private/files/view.php
index 5f19558a4fc..3ad9d36a384 100644
--- a/lib/private/files/view.php
+++ b/lib/private/files/view.php
@@ -707,10 +707,6 @@ class View {
} 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();
}
}
@@ -1179,6 +1175,11 @@ 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
@@ -1266,7 +1267,7 @@ class View {
}
$subCache = $subStorage->getCache('');
$rootEntry = $subCache->get('');
- $info->addSubEntry($rootEntry);
+ $info->addSubEntry($rootEntry, $mount->getMountPoint());
}
}
}
@@ -1357,7 +1358,7 @@ class View {
$entryName = substr($relativePath, 0, $pos);
foreach ($files as &$entry) {
if ($entry->getName() === $entryName) {
- $entry->addSubEntry($rootEntry);
+ $entry->addSubEntry($rootEntry, $mountPoint);
}
}
} else { //mountpoint in this folder, add an entry for it
diff --git a/tests/lib/files/cache/changepropagator.php b/tests/lib/files/cache/changepropagator.php
index 1b56da5e97c..9108330eb9b 100644
--- a/tests/lib/files/cache/changepropagator.php
+++ b/tests/lib/files/cache/changepropagator.php
@@ -94,4 +94,37 @@ class ChangePropagator extends \Test\TestCase {
$this->assertEquals(100, $time - $cache->get('foo')['mtime']);
$this->assertEquals(100, $time - $cache->get('foo/bar')['mtime']);
}
+
+ public function testPropagateCrossStorage() {
+ $storage = new Temporary();
+ $this->view->mkdir('/foo');
+ Filesystem::mount($storage, [], $this->view->getAbsolutePath('/foo/submount'));
+ $this->view->mkdir('/foo/submount/bar');
+ $this->view->file_put_contents('/foo/submount/bar/sad.txt', 'qwerty');
+
+ $oldInfo1 = $this->view->getFileInfo('/');
+ $oldInfo2 = $this->view->getFileInfo('/foo');
+ $oldInfo3 = $this->view->getFileInfo('/foo/submount');
+ $oldInfo4 = $this->view->getFileInfo('/foo/submount/bar');
+
+ $time = time() + 50;
+
+ $this->propagator->addChange('/foo/submount/bar/sad.txt');
+ $this->propagator->propagateChanges($time);
+
+ $newInfo1 = $this->view->getFileInfo('/');
+ $newInfo2 = $this->view->getFileInfo('/foo');
+ $newInfo3 = $this->view->getFileInfo('/foo/submount');
+ $newInfo4 = $this->view->getFileInfo('/foo/submount/bar');
+
+ $this->assertEquals($newInfo1->getMTime(), $time);
+ $this->assertEquals($newInfo2->getMTime(), $time);
+ $this->assertEquals($newInfo3->getMTime(), $time);
+ $this->assertEquals($newInfo4->getMTime(), $time);
+
+ $this->assertNotSame($oldInfo1->getEtag(), $newInfo1->getEtag());
+ $this->assertNotSame($oldInfo2->getEtag(), $newInfo2->getEtag());
+ $this->assertNotSame($oldInfo3->getEtag(), $newInfo3->getEtag());
+ $this->assertNotSame($oldInfo4->getEtag(), $newInfo3->getEtag());
+ }
}