MountProviderCollection.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  6. * @author Morris Jobke <hey@morrisjobke.de>
  7. * @author Robin Appelman <robin@icewind.nl>
  8. * @author Roeland Jago Douma <roeland@famdouma.nl>
  9. *
  10. * @license AGPL-3.0
  11. *
  12. * This code is free software: you can redistribute it and/or modify
  13. * it under the terms of the GNU Affero General Public License, version 3,
  14. * as published by the Free Software Foundation.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU Affero General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Affero General Public License, version 3,
  22. * along with this program. If not, see <http://www.gnu.org/licenses/>
  23. *
  24. */
  25. namespace OC\Files\Config;
  26. use OC\Hooks\Emitter;
  27. use OC\Hooks\EmitterTrait;
  28. use OCP\Diagnostics\IEventLogger;
  29. use OCP\Files\Config\IHomeMountProvider;
  30. use OCP\Files\Config\IMountProvider;
  31. use OCP\Files\Config\IMountProviderCollection;
  32. use OCP\Files\Config\IRootMountProvider;
  33. use OCP\Files\Config\IUserMountCache;
  34. use OCP\Files\Mount\IMountManager;
  35. use OCP\Files\Mount\IMountPoint;
  36. use OCP\Files\Storage\IStorageFactory;
  37. use OCP\IUser;
  38. class MountProviderCollection implements IMountProviderCollection, Emitter {
  39. use EmitterTrait;
  40. /**
  41. * @var \OCP\Files\Config\IHomeMountProvider[]
  42. */
  43. private $homeProviders = [];
  44. /**
  45. * @var \OCP\Files\Config\IMountProvider[]
  46. */
  47. private $providers = [];
  48. /** @var \OCP\Files\Config\IRootMountProvider[] */
  49. private $rootProviders = [];
  50. /**
  51. * @var \OCP\Files\Storage\IStorageFactory
  52. */
  53. private $loader;
  54. /**
  55. * @var \OCP\Files\Config\IUserMountCache
  56. */
  57. private $mountCache;
  58. /** @var callable[] */
  59. private $mountFilters = [];
  60. private IEventLogger $eventLogger;
  61. /**
  62. * @param \OCP\Files\Storage\IStorageFactory $loader
  63. * @param IUserMountCache $mountCache
  64. */
  65. public function __construct(
  66. IStorageFactory $loader,
  67. IUserMountCache $mountCache,
  68. IEventLogger $eventLogger
  69. ) {
  70. $this->loader = $loader;
  71. $this->mountCache = $mountCache;
  72. $this->eventLogger = $eventLogger;
  73. }
  74. private function getMountsFromProvider(IMountProvider $provider, IUser $user, IStorageFactory $loader): array {
  75. $class = str_replace('\\', '_', get_class($provider));
  76. $uid = $user->getUID();
  77. $this->eventLogger->start('fs:setup:provider:' . $class, "Getting mounts from $class for $uid");
  78. $mounts = $provider->getMountsForUser($user, $loader) ?? [];
  79. $this->eventLogger->end('fs:setup:provider:' . $class);
  80. return $mounts;
  81. }
  82. /**
  83. * @param IUser $user
  84. * @param IMountProvider[] $providers
  85. * @return IMountPoint[]
  86. */
  87. private function getUserMountsForProviders(IUser $user, array $providers): array {
  88. $loader = $this->loader;
  89. $mounts = array_map(function (IMountProvider $provider) use ($user, $loader) {
  90. return $this->getMountsFromProvider($provider, $user, $loader);
  91. }, $providers);
  92. $mounts = array_reduce($mounts, function (array $mounts, array $providerMounts) {
  93. return array_merge($mounts, $providerMounts);
  94. }, []);
  95. return $this->filterMounts($user, $mounts);
  96. }
  97. public function getMountsForUser(IUser $user): array {
  98. return $this->getUserMountsForProviders($user, $this->providers);
  99. }
  100. public function getUserMountsForProviderClasses(IUser $user, array $mountProviderClasses): array {
  101. $providers = array_filter(
  102. $this->providers,
  103. fn (IMountProvider $mountProvider) => (in_array(get_class($mountProvider), $mountProviderClasses))
  104. );
  105. return $this->getUserMountsForProviders($user, $providers);
  106. }
  107. public function addMountForUser(IUser $user, IMountManager $mountManager, callable $providerFilter = null) {
  108. // shared mount provider gets to go last since it needs to know existing files
  109. // to check for name collisions
  110. $firstMounts = [];
  111. if ($providerFilter) {
  112. $providers = array_filter($this->providers, $providerFilter);
  113. } else {
  114. $providers = $this->providers;
  115. }
  116. $firstProviders = array_filter($providers, function (IMountProvider $provider) {
  117. return (get_class($provider) !== 'OCA\Files_Sharing\MountProvider');
  118. });
  119. $lastProviders = array_filter($providers, function (IMountProvider $provider) {
  120. return (get_class($provider) === 'OCA\Files_Sharing\MountProvider');
  121. });
  122. foreach ($firstProviders as $provider) {
  123. $mounts = $this->getMountsFromProvider($provider, $user, $this->loader);
  124. $firstMounts = array_merge($firstMounts, $mounts);
  125. }
  126. $firstMounts = $this->filterMounts($user, $firstMounts);
  127. array_walk($firstMounts, [$mountManager, 'addMount']);
  128. $lateMounts = [];
  129. foreach ($lastProviders as $provider) {
  130. $mounts = $this->getMountsFromProvider($provider, $user, $this->loader);
  131. $lateMounts = array_merge($lateMounts, $mounts);
  132. }
  133. $lateMounts = $this->filterMounts($user, $lateMounts);
  134. $this->eventLogger->start("fs:setup:add-mounts", "Add mounts to the filesystem");
  135. array_walk($lateMounts, [$mountManager, 'addMount']);
  136. $this->eventLogger->end("fs:setup:add-mounts");
  137. return array_merge($lateMounts, $firstMounts);
  138. }
  139. /**
  140. * Get the configured home mount for this user
  141. *
  142. * @param \OCP\IUser $user
  143. * @return \OCP\Files\Mount\IMountPoint
  144. * @since 9.1.0
  145. */
  146. public function getHomeMountForUser(IUser $user) {
  147. /** @var \OCP\Files\Config\IHomeMountProvider[] $providers */
  148. $providers = array_reverse($this->homeProviders); // call the latest registered provider first to give apps an opportunity to overwrite builtin
  149. foreach ($providers as $homeProvider) {
  150. if ($mount = $homeProvider->getHomeMountForUser($user, $this->loader)) {
  151. $mount->setMountPoint('/' . $user->getUID()); //make sure the mountpoint is what we expect
  152. return $mount;
  153. }
  154. }
  155. throw new \Exception('No home storage configured for user ' . $user);
  156. }
  157. /**
  158. * Add a provider for mount points
  159. *
  160. * @param \OCP\Files\Config\IMountProvider $provider
  161. */
  162. public function registerProvider(IMountProvider $provider) {
  163. $this->providers[] = $provider;
  164. $this->emit('\OC\Files\Config', 'registerMountProvider', [$provider]);
  165. }
  166. public function registerMountFilter(callable $filter) {
  167. $this->mountFilters[] = $filter;
  168. }
  169. private function filterMounts(IUser $user, array $mountPoints) {
  170. return array_filter($mountPoints, function (IMountPoint $mountPoint) use ($user) {
  171. foreach ($this->mountFilters as $filter) {
  172. if ($filter($mountPoint, $user) === false) {
  173. return false;
  174. }
  175. }
  176. return true;
  177. });
  178. }
  179. /**
  180. * Add a provider for home mount points
  181. *
  182. * @param \OCP\Files\Config\IHomeMountProvider $provider
  183. * @since 9.1.0
  184. */
  185. public function registerHomeProvider(IHomeMountProvider $provider) {
  186. $this->homeProviders[] = $provider;
  187. $this->emit('\OC\Files\Config', 'registerHomeMountProvider', [$provider]);
  188. }
  189. /**
  190. * Get the mount cache which can be used to search for mounts without setting up the filesystem
  191. *
  192. * @return IUserMountCache
  193. */
  194. public function getMountCache() {
  195. return $this->mountCache;
  196. }
  197. public function registerRootProvider(IRootMountProvider $provider) {
  198. $this->rootProviders[] = $provider;
  199. }
  200. /**
  201. * Get all root mountpoints
  202. *
  203. * @return \OCP\Files\Mount\IMountPoint[]
  204. * @since 20.0.0
  205. */
  206. public function getRootMounts(): array {
  207. $loader = $this->loader;
  208. $mounts = array_map(function (IRootMountProvider $provider) use ($loader) {
  209. return $provider->getRootMounts($loader);
  210. }, $this->rootProviders);
  211. $mounts = array_reduce($mounts, function (array $mounts, array $providerMounts) {
  212. return array_merge($mounts, $providerMounts);
  213. }, []);
  214. if (count($mounts) === 0) {
  215. throw new \Exception("No root mounts provided by any provider");
  216. }
  217. return $mounts;
  218. }
  219. public function clearProviders() {
  220. $this->providers = [];
  221. $this->homeProviders = [];
  222. $this->rootProviders = [];
  223. }
  224. public function getProviders(): array {
  225. return $this->providers;
  226. }
  227. }