MountProviderCollection.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OC\Files\Config;
  8. use OC\Hooks\Emitter;
  9. use OC\Hooks\EmitterTrait;
  10. use OCP\Diagnostics\IEventLogger;
  11. use OCP\Files\Config\IHomeMountProvider;
  12. use OCP\Files\Config\IMountProvider;
  13. use OCP\Files\Config\IMountProviderCollection;
  14. use OCP\Files\Config\IRootMountProvider;
  15. use OCP\Files\Config\IUserMountCache;
  16. use OCP\Files\Mount\IMountManager;
  17. use OCP\Files\Mount\IMountPoint;
  18. use OCP\Files\Storage\IStorageFactory;
  19. use OCP\IUser;
  20. class MountProviderCollection implements IMountProviderCollection, Emitter {
  21. use EmitterTrait;
  22. /**
  23. * @var \OCP\Files\Config\IHomeMountProvider[]
  24. */
  25. private $homeProviders = [];
  26. /**
  27. * @var \OCP\Files\Config\IMountProvider[]
  28. */
  29. private $providers = [];
  30. /** @var \OCP\Files\Config\IRootMountProvider[] */
  31. private $rootProviders = [];
  32. /**
  33. * @var \OCP\Files\Storage\IStorageFactory
  34. */
  35. private $loader;
  36. /**
  37. * @var \OCP\Files\Config\IUserMountCache
  38. */
  39. private $mountCache;
  40. /** @var callable[] */
  41. private $mountFilters = [];
  42. private IEventLogger $eventLogger;
  43. /**
  44. * @param \OCP\Files\Storage\IStorageFactory $loader
  45. * @param IUserMountCache $mountCache
  46. */
  47. public function __construct(
  48. IStorageFactory $loader,
  49. IUserMountCache $mountCache,
  50. IEventLogger $eventLogger,
  51. ) {
  52. $this->loader = $loader;
  53. $this->mountCache = $mountCache;
  54. $this->eventLogger = $eventLogger;
  55. }
  56. private function getMountsFromProvider(IMountProvider $provider, IUser $user, IStorageFactory $loader): array {
  57. $class = str_replace('\\', '_', get_class($provider));
  58. $uid = $user->getUID();
  59. $this->eventLogger->start('fs:setup:provider:' . $class, "Getting mounts from $class for $uid");
  60. $mounts = $provider->getMountsForUser($user, $loader) ?? [];
  61. $this->eventLogger->end('fs:setup:provider:' . $class);
  62. return $mounts;
  63. }
  64. /**
  65. * @param IUser $user
  66. * @param IMountProvider[] $providers
  67. * @return IMountPoint[]
  68. */
  69. private function getUserMountsForProviders(IUser $user, array $providers): array {
  70. $loader = $this->loader;
  71. $mounts = array_map(function (IMountProvider $provider) use ($user, $loader) {
  72. return $this->getMountsFromProvider($provider, $user, $loader);
  73. }, $providers);
  74. $mounts = array_reduce($mounts, function (array $mounts, array $providerMounts) {
  75. return array_merge($mounts, $providerMounts);
  76. }, []);
  77. return $this->filterMounts($user, $mounts);
  78. }
  79. public function getMountsForUser(IUser $user): array {
  80. return $this->getUserMountsForProviders($user, $this->providers);
  81. }
  82. public function getUserMountsForProviderClasses(IUser $user, array $mountProviderClasses): array {
  83. $providers = array_filter(
  84. $this->providers,
  85. fn (IMountProvider $mountProvider) => (in_array(get_class($mountProvider), $mountProviderClasses))
  86. );
  87. return $this->getUserMountsForProviders($user, $providers);
  88. }
  89. public function addMountForUser(IUser $user, IMountManager $mountManager, ?callable $providerFilter = null) {
  90. // shared mount provider gets to go last since it needs to know existing files
  91. // to check for name collisions
  92. $firstMounts = [];
  93. if ($providerFilter) {
  94. $providers = array_filter($this->providers, $providerFilter);
  95. } else {
  96. $providers = $this->providers;
  97. }
  98. $firstProviders = array_filter($providers, function (IMountProvider $provider) {
  99. return (get_class($provider) !== 'OCA\Files_Sharing\MountProvider');
  100. });
  101. $lastProviders = array_filter($providers, function (IMountProvider $provider) {
  102. return (get_class($provider) === 'OCA\Files_Sharing\MountProvider');
  103. });
  104. foreach ($firstProviders as $provider) {
  105. $mounts = $this->getMountsFromProvider($provider, $user, $this->loader);
  106. $firstMounts = array_merge($firstMounts, $mounts);
  107. }
  108. $firstMounts = $this->filterMounts($user, $firstMounts);
  109. array_walk($firstMounts, [$mountManager, 'addMount']);
  110. $lateMounts = [];
  111. foreach ($lastProviders as $provider) {
  112. $mounts = $this->getMountsFromProvider($provider, $user, $this->loader);
  113. $lateMounts = array_merge($lateMounts, $mounts);
  114. }
  115. $lateMounts = $this->filterMounts($user, $lateMounts);
  116. $this->eventLogger->start('fs:setup:add-mounts', 'Add mounts to the filesystem');
  117. array_walk($lateMounts, [$mountManager, 'addMount']);
  118. $this->eventLogger->end('fs:setup:add-mounts');
  119. return array_merge($lateMounts, $firstMounts);
  120. }
  121. /**
  122. * Get the configured home mount for this user
  123. *
  124. * @param \OCP\IUser $user
  125. * @return \OCP\Files\Mount\IMountPoint
  126. * @since 9.1.0
  127. */
  128. public function getHomeMountForUser(IUser $user) {
  129. /** @var \OCP\Files\Config\IHomeMountProvider[] $providers */
  130. $providers = array_reverse($this->homeProviders); // call the latest registered provider first to give apps an opportunity to overwrite builtin
  131. foreach ($providers as $homeProvider) {
  132. if ($mount = $homeProvider->getHomeMountForUser($user, $this->loader)) {
  133. $mount->setMountPoint('/' . $user->getUID()); //make sure the mountpoint is what we expect
  134. return $mount;
  135. }
  136. }
  137. throw new \Exception('No home storage configured for user ' . $user);
  138. }
  139. /**
  140. * Add a provider for mount points
  141. *
  142. * @param \OCP\Files\Config\IMountProvider $provider
  143. */
  144. public function registerProvider(IMountProvider $provider) {
  145. $this->providers[] = $provider;
  146. $this->emit('\OC\Files\Config', 'registerMountProvider', [$provider]);
  147. }
  148. public function registerMountFilter(callable $filter) {
  149. $this->mountFilters[] = $filter;
  150. }
  151. private function filterMounts(IUser $user, array $mountPoints) {
  152. return array_filter($mountPoints, function (IMountPoint $mountPoint) use ($user) {
  153. foreach ($this->mountFilters as $filter) {
  154. if ($filter($mountPoint, $user) === false) {
  155. return false;
  156. }
  157. }
  158. return true;
  159. });
  160. }
  161. /**
  162. * Add a provider for home mount points
  163. *
  164. * @param \OCP\Files\Config\IHomeMountProvider $provider
  165. * @since 9.1.0
  166. */
  167. public function registerHomeProvider(IHomeMountProvider $provider) {
  168. $this->homeProviders[] = $provider;
  169. $this->emit('\OC\Files\Config', 'registerHomeMountProvider', [$provider]);
  170. }
  171. /**
  172. * Get the mount cache which can be used to search for mounts without setting up the filesystem
  173. *
  174. * @return IUserMountCache
  175. */
  176. public function getMountCache() {
  177. return $this->mountCache;
  178. }
  179. public function registerRootProvider(IRootMountProvider $provider) {
  180. $this->rootProviders[] = $provider;
  181. }
  182. /**
  183. * Get all root mountpoints
  184. *
  185. * @return \OCP\Files\Mount\IMountPoint[]
  186. * @since 20.0.0
  187. */
  188. public function getRootMounts(): array {
  189. $loader = $this->loader;
  190. $mounts = array_map(function (IRootMountProvider $provider) use ($loader) {
  191. return $provider->getRootMounts($loader);
  192. }, $this->rootProviders);
  193. $mounts = array_reduce($mounts, function (array $mounts, array $providerMounts) {
  194. return array_merge($mounts, $providerMounts);
  195. }, []);
  196. if (count($mounts) === 0) {
  197. throw new \Exception('No root mounts provided by any provider');
  198. }
  199. return $mounts;
  200. }
  201. public function clearProviders() {
  202. $this->providers = [];
  203. $this->homeProviders = [];
  204. $this->rootProviders = [];
  205. }
  206. public function getProviders(): array {
  207. return $this->providers;
  208. }
  209. }