Manager.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  6. * SPDX-License-Identifier: AGPL-3.0-only
  7. */
  8. namespace OC\Files\Mount;
  9. use OC\Files\Filesystem;
  10. use OC\Files\SetupManager;
  11. use OC\Files\SetupManagerFactory;
  12. use OCP\Cache\CappedMemoryCache;
  13. use OCP\Files\Config\ICachedMountInfo;
  14. use OCP\Files\Mount\IMountManager;
  15. use OCP\Files\Mount\IMountPoint;
  16. use OCP\Files\NotFoundException;
  17. class Manager implements IMountManager {
  18. /** @var MountPoint[] */
  19. private array $mounts = [];
  20. /** @var CappedMemoryCache<IMountPoint> */
  21. private CappedMemoryCache $pathCache;
  22. /** @var CappedMemoryCache<IMountPoint[]> */
  23. private CappedMemoryCache $inPathCache;
  24. private SetupManager $setupManager;
  25. public function __construct(SetupManagerFactory $setupManagerFactory) {
  26. $this->pathCache = new CappedMemoryCache();
  27. $this->inPathCache = new CappedMemoryCache();
  28. $this->setupManager = $setupManagerFactory->create($this);
  29. }
  30. /**
  31. * @param IMountPoint $mount
  32. */
  33. public function addMount(IMountPoint $mount) {
  34. $this->mounts[$mount->getMountPoint()] = $mount;
  35. $this->pathCache->clear();
  36. $this->inPathCache->clear();
  37. }
  38. /**
  39. * @param string $mountPoint
  40. */
  41. public function removeMount(string $mountPoint) {
  42. $mountPoint = Filesystem::normalizePath($mountPoint);
  43. if (\strlen($mountPoint) > 1) {
  44. $mountPoint .= '/';
  45. }
  46. unset($this->mounts[$mountPoint]);
  47. $this->pathCache->clear();
  48. $this->inPathCache->clear();
  49. }
  50. /**
  51. * @param string $mountPoint
  52. * @param string $target
  53. */
  54. public function moveMount(string $mountPoint, string $target) {
  55. $this->mounts[$target] = $this->mounts[$mountPoint];
  56. unset($this->mounts[$mountPoint]);
  57. $this->pathCache->clear();
  58. $this->inPathCache->clear();
  59. }
  60. /**
  61. * Find the mount for $path
  62. *
  63. * @param string $path
  64. * @return IMountPoint
  65. */
  66. public function find(string $path): IMountPoint {
  67. $this->setupManager->setupForPath($path);
  68. $path = Filesystem::normalizePath($path);
  69. if (isset($this->pathCache[$path])) {
  70. return $this->pathCache[$path];
  71. }
  72. if (count($this->mounts) === 0) {
  73. $this->setupManager->setupRoot();
  74. if (count($this->mounts) === 0) {
  75. throw new \Exception('No mounts even after explicitly setting up the root mounts');
  76. }
  77. }
  78. $current = $path;
  79. while (true) {
  80. $mountPoint = $current . '/';
  81. if (isset($this->mounts[$mountPoint])) {
  82. $this->pathCache[$path] = $this->mounts[$mountPoint];
  83. return $this->mounts[$mountPoint];
  84. } elseif ($current === '') {
  85. break;
  86. }
  87. $current = dirname($current);
  88. if ($current === '.' || $current === '/') {
  89. $current = '';
  90. }
  91. }
  92. throw new NotFoundException('No mount for path ' . $path . ' existing mounts (' . count($this->mounts) . '): ' . implode(',', array_keys($this->mounts)));
  93. }
  94. /**
  95. * Find all mounts in $path
  96. *
  97. * @param string $path
  98. * @return IMountPoint[]
  99. */
  100. public function findIn(string $path): array {
  101. $this->setupManager->setupForPath($path, true);
  102. $path = $this->formatPath($path);
  103. if (isset($this->inPathCache[$path])) {
  104. return $this->inPathCache[$path];
  105. }
  106. $result = [];
  107. $pathLength = \strlen($path);
  108. $mountPoints = array_keys($this->mounts);
  109. foreach ($mountPoints as $mountPoint) {
  110. if (substr($mountPoint, 0, $pathLength) === $path && \strlen($mountPoint) > $pathLength) {
  111. $result[] = $this->mounts[$mountPoint];
  112. }
  113. }
  114. $this->inPathCache[$path] = $result;
  115. return $result;
  116. }
  117. public function clear() {
  118. $this->mounts = [];
  119. $this->pathCache->clear();
  120. $this->inPathCache->clear();
  121. }
  122. /**
  123. * Find mounts by storage id
  124. *
  125. * @param string $id
  126. * @return IMountPoint[]
  127. */
  128. public function findByStorageId(string $id): array {
  129. if (\strlen($id) > 64) {
  130. $id = md5($id);
  131. }
  132. $result = [];
  133. foreach ($this->mounts as $mount) {
  134. if ($mount->getStorageId() === $id) {
  135. $result[] = $mount;
  136. }
  137. }
  138. return $result;
  139. }
  140. /**
  141. * @return IMountPoint[]
  142. */
  143. public function getAll(): array {
  144. return $this->mounts;
  145. }
  146. /**
  147. * Find mounts by numeric storage id
  148. *
  149. * @param int $id
  150. * @return IMountPoint[]
  151. */
  152. public function findByNumericId(int $id): array {
  153. $result = [];
  154. foreach ($this->mounts as $mount) {
  155. if ($mount->getNumericStorageId() === $id) {
  156. $result[] = $mount;
  157. }
  158. }
  159. return $result;
  160. }
  161. /**
  162. * @param string $path
  163. * @return string
  164. */
  165. private function formatPath(string $path): string {
  166. $path = Filesystem::normalizePath($path);
  167. if (\strlen($path) > 1) {
  168. $path .= '/';
  169. }
  170. return $path;
  171. }
  172. public function getSetupManager(): SetupManager {
  173. return $this->setupManager;
  174. }
  175. /**
  176. * Return all mounts in a path from a specific mount provider
  177. *
  178. * @param string $path
  179. * @param string[] $mountProviders
  180. * @return MountPoint[]
  181. */
  182. public function getMountsByMountProvider(string $path, array $mountProviders) {
  183. $this->getSetupManager()->setupForProvider($path, $mountProviders);
  184. if (in_array('', $mountProviders)) {
  185. return $this->mounts;
  186. } else {
  187. return array_filter($this->mounts, function ($mount) use ($mountProviders) {
  188. return in_array($mount->getMountProvider(), $mountProviders);
  189. });
  190. }
  191. }
  192. /**
  193. * Return the mount matching a cached mount info (or mount file info)
  194. *
  195. * @param ICachedMountInfo $info
  196. *
  197. * @return IMountPoint|null
  198. */
  199. public function getMountFromMountInfo(ICachedMountInfo $info): ?IMountPoint {
  200. $this->setupManager->setupForPath($info->getMountPoint());
  201. foreach ($this->mounts as $mount) {
  202. if ($mount->getMountPoint() === $info->getMountPoint()) {
  203. return $mount;
  204. }
  205. }
  206. return null;
  207. }
  208. }