@@ -63,547 +63,547 @@ |
||
63 | 63 | use Psr\Log\LoggerInterface; |
64 | 64 | |
65 | 65 | class SetupManager { |
66 | - private bool $rootSetup = false; |
|
67 | - private IEventLogger $eventLogger; |
|
68 | - private MountProviderCollection $mountProviderCollection; |
|
69 | - private IMountManager $mountManager; |
|
70 | - private IUserManager $userManager; |
|
71 | - // List of users for which at least one mount is setup |
|
72 | - private array $setupUsers = []; |
|
73 | - // List of users for which all mounts are setup |
|
74 | - private array $setupUsersComplete = []; |
|
75 | - /** @var array<string, string[]> */ |
|
76 | - private array $setupUserMountProviders = []; |
|
77 | - private IEventDispatcher $eventDispatcher; |
|
78 | - private IUserMountCache $userMountCache; |
|
79 | - private ILockdownManager $lockdownManager; |
|
80 | - private IUserSession $userSession; |
|
81 | - private ICache $cache; |
|
82 | - private LoggerInterface $logger; |
|
83 | - private IConfig $config; |
|
84 | - private bool $listeningForProviders; |
|
85 | - private array $fullSetupRequired = []; |
|
86 | - private bool $setupBuiltinWrappersDone = false; |
|
87 | - |
|
88 | - public function __construct( |
|
89 | - IEventLogger $eventLogger, |
|
90 | - MountProviderCollection $mountProviderCollection, |
|
91 | - IMountManager $mountManager, |
|
92 | - IUserManager $userManager, |
|
93 | - IEventDispatcher $eventDispatcher, |
|
94 | - IUserMountCache $userMountCache, |
|
95 | - ILockdownManager $lockdownManager, |
|
96 | - IUserSession $userSession, |
|
97 | - ICacheFactory $cacheFactory, |
|
98 | - LoggerInterface $logger, |
|
99 | - IConfig $config |
|
100 | - ) { |
|
101 | - $this->eventLogger = $eventLogger; |
|
102 | - $this->mountProviderCollection = $mountProviderCollection; |
|
103 | - $this->mountManager = $mountManager; |
|
104 | - $this->userManager = $userManager; |
|
105 | - $this->eventDispatcher = $eventDispatcher; |
|
106 | - $this->userMountCache = $userMountCache; |
|
107 | - $this->lockdownManager = $lockdownManager; |
|
108 | - $this->logger = $logger; |
|
109 | - $this->userSession = $userSession; |
|
110 | - $this->cache = $cacheFactory->createDistributed('setupmanager::'); |
|
111 | - $this->listeningForProviders = false; |
|
112 | - $this->config = $config; |
|
113 | - |
|
114 | - $this->setupListeners(); |
|
115 | - } |
|
116 | - |
|
117 | - private function isSetupStarted(IUser $user): bool { |
|
118 | - return in_array($user->getUID(), $this->setupUsers, true); |
|
119 | - } |
|
120 | - |
|
121 | - public function isSetupComplete(IUser $user): bool { |
|
122 | - return in_array($user->getUID(), $this->setupUsersComplete, true); |
|
123 | - } |
|
124 | - |
|
125 | - private function setupBuiltinWrappers() { |
|
126 | - if ($this->setupBuiltinWrappersDone) { |
|
127 | - return; |
|
128 | - } |
|
129 | - $this->setupBuiltinWrappersDone = true; |
|
130 | - |
|
131 | - // load all filesystem apps before, so no setup-hook gets lost |
|
132 | - OC_App::loadApps(['filesystem']); |
|
133 | - $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false); |
|
134 | - |
|
135 | - Filesystem::addStorageWrapper('mount_options', function ($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
136 | - if ($storage->instanceOfStorage(Common::class)) { |
|
137 | - $storage->setMountOptions($mount->getOptions()); |
|
138 | - } |
|
139 | - return $storage; |
|
140 | - }); |
|
141 | - |
|
142 | - Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
143 | - if (!$mount->getOption('enable_sharing', true)) { |
|
144 | - return new PermissionsMask([ |
|
145 | - 'storage' => $storage, |
|
146 | - 'mask' => Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE, |
|
147 | - ]); |
|
148 | - } |
|
149 | - return $storage; |
|
150 | - }); |
|
151 | - |
|
152 | - // install storage availability wrapper, before most other wrappers |
|
153 | - Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, IStorage $storage) { |
|
154 | - if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) { |
|
155 | - return new Availability(['storage' => $storage]); |
|
156 | - } |
|
157 | - return $storage; |
|
158 | - }); |
|
159 | - |
|
160 | - Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
161 | - if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) { |
|
162 | - return new Encoding(['storage' => $storage]); |
|
163 | - } |
|
164 | - return $storage; |
|
165 | - }); |
|
166 | - |
|
167 | - Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) { |
|
168 | - // set up quota for home storages, even for other users |
|
169 | - // which can happen when using sharing |
|
170 | - |
|
171 | - /** |
|
172 | - * @var Storage $storage |
|
173 | - */ |
|
174 | - if ($storage->instanceOfStorage(HomeObjectStoreStorage::class) || $storage->instanceOfStorage(Home::class)) { |
|
175 | - if (is_object($storage->getUser())) { |
|
176 | - $user = $storage->getUser(); |
|
177 | - return new Quota(['storage' => $storage, 'quotaCallback' => function () use ($user) { |
|
178 | - return OC_Util::getUserQuota($user); |
|
179 | - }, 'root' => 'files']); |
|
180 | - } |
|
181 | - } |
|
182 | - |
|
183 | - return $storage; |
|
184 | - }); |
|
185 | - |
|
186 | - Filesystem::addStorageWrapper('readonly', function ($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
187 | - /* |
|
66 | + private bool $rootSetup = false; |
|
67 | + private IEventLogger $eventLogger; |
|
68 | + private MountProviderCollection $mountProviderCollection; |
|
69 | + private IMountManager $mountManager; |
|
70 | + private IUserManager $userManager; |
|
71 | + // List of users for which at least one mount is setup |
|
72 | + private array $setupUsers = []; |
|
73 | + // List of users for which all mounts are setup |
|
74 | + private array $setupUsersComplete = []; |
|
75 | + /** @var array<string, string[]> */ |
|
76 | + private array $setupUserMountProviders = []; |
|
77 | + private IEventDispatcher $eventDispatcher; |
|
78 | + private IUserMountCache $userMountCache; |
|
79 | + private ILockdownManager $lockdownManager; |
|
80 | + private IUserSession $userSession; |
|
81 | + private ICache $cache; |
|
82 | + private LoggerInterface $logger; |
|
83 | + private IConfig $config; |
|
84 | + private bool $listeningForProviders; |
|
85 | + private array $fullSetupRequired = []; |
|
86 | + private bool $setupBuiltinWrappersDone = false; |
|
87 | + |
|
88 | + public function __construct( |
|
89 | + IEventLogger $eventLogger, |
|
90 | + MountProviderCollection $mountProviderCollection, |
|
91 | + IMountManager $mountManager, |
|
92 | + IUserManager $userManager, |
|
93 | + IEventDispatcher $eventDispatcher, |
|
94 | + IUserMountCache $userMountCache, |
|
95 | + ILockdownManager $lockdownManager, |
|
96 | + IUserSession $userSession, |
|
97 | + ICacheFactory $cacheFactory, |
|
98 | + LoggerInterface $logger, |
|
99 | + IConfig $config |
|
100 | + ) { |
|
101 | + $this->eventLogger = $eventLogger; |
|
102 | + $this->mountProviderCollection = $mountProviderCollection; |
|
103 | + $this->mountManager = $mountManager; |
|
104 | + $this->userManager = $userManager; |
|
105 | + $this->eventDispatcher = $eventDispatcher; |
|
106 | + $this->userMountCache = $userMountCache; |
|
107 | + $this->lockdownManager = $lockdownManager; |
|
108 | + $this->logger = $logger; |
|
109 | + $this->userSession = $userSession; |
|
110 | + $this->cache = $cacheFactory->createDistributed('setupmanager::'); |
|
111 | + $this->listeningForProviders = false; |
|
112 | + $this->config = $config; |
|
113 | + |
|
114 | + $this->setupListeners(); |
|
115 | + } |
|
116 | + |
|
117 | + private function isSetupStarted(IUser $user): bool { |
|
118 | + return in_array($user->getUID(), $this->setupUsers, true); |
|
119 | + } |
|
120 | + |
|
121 | + public function isSetupComplete(IUser $user): bool { |
|
122 | + return in_array($user->getUID(), $this->setupUsersComplete, true); |
|
123 | + } |
|
124 | + |
|
125 | + private function setupBuiltinWrappers() { |
|
126 | + if ($this->setupBuiltinWrappersDone) { |
|
127 | + return; |
|
128 | + } |
|
129 | + $this->setupBuiltinWrappersDone = true; |
|
130 | + |
|
131 | + // load all filesystem apps before, so no setup-hook gets lost |
|
132 | + OC_App::loadApps(['filesystem']); |
|
133 | + $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false); |
|
134 | + |
|
135 | + Filesystem::addStorageWrapper('mount_options', function ($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
136 | + if ($storage->instanceOfStorage(Common::class)) { |
|
137 | + $storage->setMountOptions($mount->getOptions()); |
|
138 | + } |
|
139 | + return $storage; |
|
140 | + }); |
|
141 | + |
|
142 | + Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
143 | + if (!$mount->getOption('enable_sharing', true)) { |
|
144 | + return new PermissionsMask([ |
|
145 | + 'storage' => $storage, |
|
146 | + 'mask' => Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE, |
|
147 | + ]); |
|
148 | + } |
|
149 | + return $storage; |
|
150 | + }); |
|
151 | + |
|
152 | + // install storage availability wrapper, before most other wrappers |
|
153 | + Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, IStorage $storage) { |
|
154 | + if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) { |
|
155 | + return new Availability(['storage' => $storage]); |
|
156 | + } |
|
157 | + return $storage; |
|
158 | + }); |
|
159 | + |
|
160 | + Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
161 | + if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) { |
|
162 | + return new Encoding(['storage' => $storage]); |
|
163 | + } |
|
164 | + return $storage; |
|
165 | + }); |
|
166 | + |
|
167 | + Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) { |
|
168 | + // set up quota for home storages, even for other users |
|
169 | + // which can happen when using sharing |
|
170 | + |
|
171 | + /** |
|
172 | + * @var Storage $storage |
|
173 | + */ |
|
174 | + if ($storage->instanceOfStorage(HomeObjectStoreStorage::class) || $storage->instanceOfStorage(Home::class)) { |
|
175 | + if (is_object($storage->getUser())) { |
|
176 | + $user = $storage->getUser(); |
|
177 | + return new Quota(['storage' => $storage, 'quotaCallback' => function () use ($user) { |
|
178 | + return OC_Util::getUserQuota($user); |
|
179 | + }, 'root' => 'files']); |
|
180 | + } |
|
181 | + } |
|
182 | + |
|
183 | + return $storage; |
|
184 | + }); |
|
185 | + |
|
186 | + Filesystem::addStorageWrapper('readonly', function ($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
187 | + /* |
|
188 | 188 | * Do not allow any operations that modify the storage |
189 | 189 | */ |
190 | - if ($mount->getOption('readonly', false)) { |
|
191 | - return new PermissionsMask([ |
|
192 | - 'storage' => $storage, |
|
193 | - 'mask' => Constants::PERMISSION_ALL & ~( |
|
194 | - Constants::PERMISSION_UPDATE | |
|
195 | - Constants::PERMISSION_CREATE | |
|
196 | - Constants::PERMISSION_DELETE |
|
197 | - ), |
|
198 | - ]); |
|
199 | - } |
|
200 | - return $storage; |
|
201 | - }); |
|
202 | - |
|
203 | - Filesystem::logWarningWhenAddingStorageWrapper($prevLogging); |
|
204 | - } |
|
205 | - |
|
206 | - /** |
|
207 | - * Setup the full filesystem for the specified user |
|
208 | - */ |
|
209 | - public function setupForUser(IUser $user): void { |
|
210 | - if ($this->isSetupComplete($user)) { |
|
211 | - return; |
|
212 | - } |
|
213 | - $this->setupUsersComplete[] = $user->getUID(); |
|
214 | - |
|
215 | - $this->eventLogger->start('fs:setup:user:full', 'Setup full filesystem for user'); |
|
216 | - |
|
217 | - if (!isset($this->setupUserMountProviders[$user->getUID()])) { |
|
218 | - $this->setupUserMountProviders[$user->getUID()] = []; |
|
219 | - } |
|
220 | - |
|
221 | - $previouslySetupProviders = $this->setupUserMountProviders[$user->getUID()]; |
|
222 | - |
|
223 | - $this->setupForUserWith($user, function () use ($user) { |
|
224 | - $this->mountProviderCollection->addMountForUser($user, $this->mountManager, function ( |
|
225 | - IMountProvider $provider |
|
226 | - ) use ($user) { |
|
227 | - return !in_array(get_class($provider), $this->setupUserMountProviders[$user->getUID()]); |
|
228 | - }); |
|
229 | - }); |
|
230 | - $this->afterUserFullySetup($user, $previouslySetupProviders); |
|
231 | - $this->eventLogger->end('fs:setup:user:full'); |
|
232 | - } |
|
233 | - |
|
234 | - /** |
|
235 | - * part of the user setup that is run only once per user |
|
236 | - */ |
|
237 | - private function oneTimeUserSetup(IUser $user) { |
|
238 | - if ($this->isSetupStarted($user)) { |
|
239 | - return; |
|
240 | - } |
|
241 | - $this->setupUsers[] = $user->getUID(); |
|
242 | - |
|
243 | - $this->setupRoot(); |
|
244 | - |
|
245 | - $this->eventLogger->start('fs:setup:user:onetime', 'Onetime filesystem for user'); |
|
246 | - |
|
247 | - $this->setupBuiltinWrappers(); |
|
248 | - |
|
249 | - $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false); |
|
250 | - |
|
251 | - OC_Hook::emit('OC_Filesystem', 'preSetup', ['user' => $user->getUID()]); |
|
252 | - |
|
253 | - Filesystem::logWarningWhenAddingStorageWrapper($prevLogging); |
|
254 | - |
|
255 | - $userDir = '/' . $user->getUID() . '/files'; |
|
256 | - |
|
257 | - Filesystem::initInternal($userDir); |
|
258 | - |
|
259 | - if ($this->lockdownManager->canAccessFilesystem()) { |
|
260 | - $this->eventLogger->start('fs:setup:user:home', 'Setup home filesystem for user'); |
|
261 | - // home mounts are handled separate since we need to ensure this is mounted before we call the other mount providers |
|
262 | - $homeMount = $this->mountProviderCollection->getHomeMountForUser($user); |
|
263 | - $this->mountManager->addMount($homeMount); |
|
264 | - |
|
265 | - if ($homeMount->getStorageRootId() === -1) { |
|
266 | - $this->eventLogger->start('fs:setup:user:home:scan', 'Scan home filesystem for user'); |
|
267 | - $homeMount->getStorage()->mkdir(''); |
|
268 | - $homeMount->getStorage()->getScanner()->scan(''); |
|
269 | - $this->eventLogger->end('fs:setup:user:home:scan'); |
|
270 | - } |
|
271 | - $this->eventLogger->end('fs:setup:user:home'); |
|
272 | - } else { |
|
273 | - $this->mountManager->addMount(new MountPoint( |
|
274 | - new NullStorage([]), |
|
275 | - '/' . $user->getUID() |
|
276 | - )); |
|
277 | - $this->mountManager->addMount(new MountPoint( |
|
278 | - new NullStorage([]), |
|
279 | - '/' . $user->getUID() . '/files' |
|
280 | - )); |
|
281 | - $this->setupUsersComplete[] = $user->getUID(); |
|
282 | - } |
|
283 | - |
|
284 | - $this->listenForNewMountProviders(); |
|
285 | - |
|
286 | - $this->eventLogger->end('fs:setup:user:onetime'); |
|
287 | - } |
|
288 | - |
|
289 | - /** |
|
290 | - * Final housekeeping after a user has been fully setup |
|
291 | - */ |
|
292 | - private function afterUserFullySetup(IUser $user, array $previouslySetupProviders): void { |
|
293 | - $this->eventLogger->start('fs:setup:user:full:post', 'Housekeeping after user is setup'); |
|
294 | - $userRoot = '/' . $user->getUID() . '/'; |
|
295 | - $mounts = $this->mountManager->getAll(); |
|
296 | - $mounts = array_filter($mounts, function (IMountPoint $mount) use ($userRoot) { |
|
297 | - return strpos($mount->getMountPoint(), $userRoot) === 0; |
|
298 | - }); |
|
299 | - $allProviders = array_map(function (IMountProvider $provider) { |
|
300 | - return get_class($provider); |
|
301 | - }, $this->mountProviderCollection->getProviders()); |
|
302 | - $newProviders = array_diff($allProviders, $previouslySetupProviders); |
|
303 | - $mounts = array_filter($mounts, function (IMountPoint $mount) use ($previouslySetupProviders) { |
|
304 | - return !in_array($mount->getMountProvider(), $previouslySetupProviders); |
|
305 | - }); |
|
306 | - $this->userMountCache->registerMounts($user, $mounts, $newProviders); |
|
307 | - |
|
308 | - $cacheDuration = $this->config->getSystemValueInt('fs_mount_cache_duration', 5 * 60); |
|
309 | - if ($cacheDuration > 0) { |
|
310 | - $this->cache->set($user->getUID(), true, $cacheDuration); |
|
311 | - $this->fullSetupRequired[$user->getUID()] = false; |
|
312 | - } |
|
313 | - $this->eventLogger->end('fs:setup:user:full:post'); |
|
314 | - } |
|
315 | - |
|
316 | - /** |
|
317 | - * @param IUser $user |
|
318 | - * @param IMountPoint $mounts |
|
319 | - * @return void |
|
320 | - * @throws \OCP\HintException |
|
321 | - * @throws \OC\ServerNotAvailableException |
|
322 | - */ |
|
323 | - private function setupForUserWith(IUser $user, callable $mountCallback): void { |
|
324 | - $this->oneTimeUserSetup($user); |
|
325 | - |
|
326 | - if ($this->lockdownManager->canAccessFilesystem()) { |
|
327 | - $mountCallback(); |
|
328 | - } |
|
329 | - $this->eventLogger->start('fs:setup:user:post-init-mountpoint', 'post_initMountPoints legacy hook'); |
|
330 | - \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', ['user' => $user->getUID()]); |
|
331 | - $this->eventLogger->end('fs:setup:user:post-init-mountpoint'); |
|
332 | - |
|
333 | - $userDir = '/' . $user->getUID() . '/files'; |
|
334 | - $this->eventLogger->start('fs:setup:user:setup-hook', 'setup legacy hook'); |
|
335 | - OC_Hook::emit('OC_Filesystem', 'setup', ['user' => $user->getUID(), 'user_dir' => $userDir]); |
|
336 | - $this->eventLogger->end('fs:setup:user:setup-hook'); |
|
337 | - } |
|
338 | - |
|
339 | - /** |
|
340 | - * Set up the root filesystem |
|
341 | - */ |
|
342 | - public function setupRoot(): void { |
|
343 | - //setting up the filesystem twice can only lead to trouble |
|
344 | - if ($this->rootSetup) { |
|
345 | - return; |
|
346 | - } |
|
347 | - $this->rootSetup = true; |
|
348 | - |
|
349 | - $this->eventLogger->start('fs:setup:root', 'Setup root filesystem'); |
|
350 | - |
|
351 | - $this->setupBuiltinWrappers(); |
|
352 | - |
|
353 | - $rootMounts = $this->mountProviderCollection->getRootMounts(); |
|
354 | - foreach ($rootMounts as $rootMountProvider) { |
|
355 | - $this->mountManager->addMount($rootMountProvider); |
|
356 | - } |
|
357 | - |
|
358 | - $this->eventLogger->end('fs:setup:root'); |
|
359 | - } |
|
360 | - |
|
361 | - /** |
|
362 | - * Get the user to setup for a path or `null` if the root needs to be setup |
|
363 | - * |
|
364 | - * @param string $path |
|
365 | - * @return IUser|null |
|
366 | - */ |
|
367 | - private function getUserForPath(string $path) { |
|
368 | - if (strpos($path, '/__groupfolders') === 0) { |
|
369 | - return null; |
|
370 | - } elseif (substr_count($path, '/') < 2) { |
|
371 | - if ($user = $this->userSession->getUser()) { |
|
372 | - return $user; |
|
373 | - } else { |
|
374 | - return null; |
|
375 | - } |
|
376 | - } elseif (strpos($path, '/appdata_' . \OC_Util::getInstanceId()) === 0 || strpos($path, '/files_external/') === 0) { |
|
377 | - return null; |
|
378 | - } else { |
|
379 | - [, $userId] = explode('/', $path); |
|
380 | - } |
|
381 | - |
|
382 | - return $this->userManager->get($userId); |
|
383 | - } |
|
384 | - |
|
385 | - /** |
|
386 | - * Set up the filesystem for the specified path |
|
387 | - */ |
|
388 | - public function setupForPath(string $path, bool $includeChildren = false): void { |
|
389 | - $user = $this->getUserForPath($path); |
|
390 | - if (!$user) { |
|
391 | - $this->setupRoot(); |
|
392 | - return; |
|
393 | - } |
|
394 | - |
|
395 | - if ($this->isSetupComplete($user)) { |
|
396 | - return; |
|
397 | - } |
|
398 | - |
|
399 | - if ($this->fullSetupRequired($user)) { |
|
400 | - $this->setupForUser($user); |
|
401 | - return; |
|
402 | - } |
|
403 | - |
|
404 | - // for the user's home folder, and includes children we need everything always |
|
405 | - if (rtrim($path) === "/" . $user->getUID() . "/files" && $includeChildren) { |
|
406 | - $this->setupForUser($user); |
|
407 | - return; |
|
408 | - } |
|
409 | - |
|
410 | - if (!isset($this->setupUserMountProviders[$user->getUID()])) { |
|
411 | - $this->setupUserMountProviders[$user->getUID()] = []; |
|
412 | - } |
|
413 | - $setupProviders = &$this->setupUserMountProviders[$user->getUID()]; |
|
414 | - $currentProviders = []; |
|
415 | - |
|
416 | - try { |
|
417 | - $cachedMount = $this->userMountCache->getMountForPath($user, $path); |
|
418 | - } catch (NotFoundException $e) { |
|
419 | - $this->setupForUser($user); |
|
420 | - return; |
|
421 | - } |
|
422 | - |
|
423 | - $this->oneTimeUserSetup($user); |
|
424 | - |
|
425 | - $this->eventLogger->start('fs:setup:user:path', "Setup $path filesystem for user"); |
|
426 | - $this->eventLogger->start('fs:setup:user:path:find', "Find mountpoint for $path"); |
|
427 | - |
|
428 | - $mounts = []; |
|
429 | - if (!in_array($cachedMount->getMountProvider(), $setupProviders)) { |
|
430 | - $currentProviders[] = $cachedMount->getMountProvider(); |
|
431 | - if ($cachedMount->getMountProvider()) { |
|
432 | - $setupProviders[] = $cachedMount->getMountProvider(); |
|
433 | - $mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$cachedMount->getMountProvider()]); |
|
434 | - } else { |
|
435 | - $this->logger->debug("mount at " . $cachedMount->getMountPoint() . " has no provider set, performing full setup"); |
|
436 | - $this->eventLogger->end('fs:setup:user:path:find'); |
|
437 | - $this->setupForUser($user); |
|
438 | - $this->eventLogger->end('fs:setup:user:path'); |
|
439 | - return; |
|
440 | - } |
|
441 | - } |
|
442 | - |
|
443 | - if ($includeChildren) { |
|
444 | - $subCachedMounts = $this->userMountCache->getMountsInPath($user, $path); |
|
445 | - $this->eventLogger->end('fs:setup:user:path:find'); |
|
446 | - |
|
447 | - $needsFullSetup = array_reduce($subCachedMounts, function (bool $needsFullSetup, ICachedMountInfo $cachedMountInfo) { |
|
448 | - return $needsFullSetup || $cachedMountInfo->getMountProvider() === ''; |
|
449 | - }, false); |
|
450 | - |
|
451 | - if ($needsFullSetup) { |
|
452 | - $this->logger->debug("mount has no provider set, performing full setup"); |
|
453 | - $this->setupForUser($user); |
|
454 | - $this->eventLogger->end('fs:setup:user:path'); |
|
455 | - return; |
|
456 | - } else { |
|
457 | - foreach ($subCachedMounts as $cachedMount) { |
|
458 | - if (!in_array($cachedMount->getMountProvider(), $setupProviders)) { |
|
459 | - $currentProviders[] = $cachedMount->getMountProvider(); |
|
460 | - $setupProviders[] = $cachedMount->getMountProvider(); |
|
461 | - $mounts = array_merge($mounts, $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$cachedMount->getMountProvider()])); |
|
462 | - } |
|
463 | - } |
|
464 | - } |
|
465 | - } else { |
|
466 | - $this->eventLogger->end('fs:setup:user:path:find'); |
|
467 | - } |
|
468 | - |
|
469 | - if (count($mounts)) { |
|
470 | - $this->userMountCache->registerMounts($user, $mounts, $currentProviders); |
|
471 | - $this->setupForUserWith($user, function () use ($mounts) { |
|
472 | - array_walk($mounts, [$this->mountManager, 'addMount']); |
|
473 | - }); |
|
474 | - } elseif (!$this->isSetupStarted($user)) { |
|
475 | - $this->oneTimeUserSetup($user); |
|
476 | - } |
|
477 | - $this->eventLogger->end('fs:setup:user:path'); |
|
478 | - } |
|
479 | - |
|
480 | - private function fullSetupRequired(IUser $user): bool { |
|
481 | - // we perform a "cached" setup only after having done the full setup recently |
|
482 | - // this is also used to trigger a full setup after handling events that are likely |
|
483 | - // to change the available mounts |
|
484 | - if (!isset($this->fullSetupRequired[$user->getUID()])) { |
|
485 | - $this->fullSetupRequired[$user->getUID()] = !$this->cache->get($user->getUID()); |
|
486 | - } |
|
487 | - return $this->fullSetupRequired[$user->getUID()]; |
|
488 | - } |
|
489 | - |
|
490 | - /** |
|
491 | - * @param string $path |
|
492 | - * @param string[] $providers |
|
493 | - */ |
|
494 | - public function setupForProvider(string $path, array $providers): void { |
|
495 | - $user = $this->getUserForPath($path); |
|
496 | - if (!$user) { |
|
497 | - $this->setupRoot(); |
|
498 | - return; |
|
499 | - } |
|
500 | - |
|
501 | - if ($this->isSetupComplete($user)) { |
|
502 | - return; |
|
503 | - } |
|
504 | - |
|
505 | - if ($this->fullSetupRequired($user)) { |
|
506 | - $this->setupForUser($user); |
|
507 | - return; |
|
508 | - } |
|
509 | - |
|
510 | - $this->eventLogger->start('fs:setup:user:providers', "Setup filesystem for " . implode(', ', $providers)); |
|
511 | - |
|
512 | - $this->oneTimeUserSetup($user); |
|
513 | - |
|
514 | - // home providers are always used |
|
515 | - $providers = array_filter($providers, function (string $provider) { |
|
516 | - return !is_subclass_of($provider, IHomeMountProvider::class); |
|
517 | - }); |
|
518 | - |
|
519 | - if (in_array('', $providers)) { |
|
520 | - $this->setupForUser($user); |
|
521 | - return; |
|
522 | - } |
|
523 | - $setupProviders = $this->setupUserMountProviders[$user->getUID()] ?? []; |
|
524 | - |
|
525 | - $providers = array_diff($providers, $setupProviders); |
|
526 | - if (count($providers) === 0) { |
|
527 | - if (!$this->isSetupStarted($user)) { |
|
528 | - $this->oneTimeUserSetup($user); |
|
529 | - } |
|
530 | - $this->eventLogger->end('fs:setup:user:providers'); |
|
531 | - return; |
|
532 | - } else { |
|
533 | - $this->setupUserMountProviders[$user->getUID()] = array_merge($setupProviders, $providers); |
|
534 | - $mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, $providers); |
|
535 | - } |
|
536 | - |
|
537 | - $this->userMountCache->registerMounts($user, $mounts, $providers); |
|
538 | - $this->setupForUserWith($user, function () use ($mounts) { |
|
539 | - array_walk($mounts, [$this->mountManager, 'addMount']); |
|
540 | - }); |
|
541 | - $this->eventLogger->end('fs:setup:user:providers'); |
|
542 | - } |
|
543 | - |
|
544 | - public function tearDown() { |
|
545 | - $this->setupUsers = []; |
|
546 | - $this->setupUsersComplete = []; |
|
547 | - $this->setupUserMountProviders = []; |
|
548 | - $this->fullSetupRequired = []; |
|
549 | - $this->rootSetup = false; |
|
550 | - $this->mountManager->clear(); |
|
551 | - $this->eventDispatcher->dispatchTyped(new FilesystemTornDownEvent()); |
|
552 | - } |
|
553 | - |
|
554 | - /** |
|
555 | - * Get mounts from mount providers that are registered after setup |
|
556 | - */ |
|
557 | - private function listenForNewMountProviders() { |
|
558 | - if (!$this->listeningForProviders) { |
|
559 | - $this->listeningForProviders = true; |
|
560 | - $this->mountProviderCollection->listen('\OC\Files\Config', 'registerMountProvider', function ( |
|
561 | - IMountProvider $provider |
|
562 | - ) { |
|
563 | - foreach ($this->setupUsers as $userId) { |
|
564 | - $user = $this->userManager->get($userId); |
|
565 | - if ($user) { |
|
566 | - $mounts = $provider->getMountsForUser($user, Filesystem::getLoader()); |
|
567 | - array_walk($mounts, [$this->mountManager, 'addMount']); |
|
568 | - } |
|
569 | - } |
|
570 | - }); |
|
571 | - } |
|
572 | - } |
|
573 | - |
|
574 | - private function setupListeners() { |
|
575 | - // note that this event handling is intentionally pessimistic |
|
576 | - // clearing the cache to often is better than not enough |
|
577 | - |
|
578 | - $this->eventDispatcher->addListener(UserAddedEvent::class, function (UserAddedEvent $event) { |
|
579 | - $this->cache->remove($event->getUser()->getUID()); |
|
580 | - }); |
|
581 | - $this->eventDispatcher->addListener(UserRemovedEvent::class, function (UserRemovedEvent $event) { |
|
582 | - $this->cache->remove($event->getUser()->getUID()); |
|
583 | - }); |
|
584 | - $this->eventDispatcher->addListener(ShareCreatedEvent::class, function (ShareCreatedEvent $event) { |
|
585 | - $this->cache->remove($event->getShare()->getSharedWith()); |
|
586 | - }); |
|
587 | - $this->eventDispatcher->addListener(InvalidateMountCacheEvent::class, function (InvalidateMountCacheEvent $event |
|
588 | - ) { |
|
589 | - if ($user = $event->getUser()) { |
|
590 | - $this->cache->remove($user->getUID()); |
|
591 | - } else { |
|
592 | - $this->cache->clear(); |
|
593 | - } |
|
594 | - }); |
|
595 | - |
|
596 | - $genericEvents = [ |
|
597 | - 'OCA\Circles\Events\CreatingCircleEvent', |
|
598 | - 'OCA\Circles\Events\DestroyingCircleEvent', |
|
599 | - 'OCA\Circles\Events\AddingCircleMemberEvent', |
|
600 | - 'OCA\Circles\Events\RemovingCircleMemberEvent', |
|
601 | - ]; |
|
602 | - |
|
603 | - foreach ($genericEvents as $genericEvent) { |
|
604 | - $this->eventDispatcher->addListener($genericEvent, function ($event) { |
|
605 | - $this->cache->clear(); |
|
606 | - }); |
|
607 | - } |
|
608 | - } |
|
190 | + if ($mount->getOption('readonly', false)) { |
|
191 | + return new PermissionsMask([ |
|
192 | + 'storage' => $storage, |
|
193 | + 'mask' => Constants::PERMISSION_ALL & ~( |
|
194 | + Constants::PERMISSION_UPDATE | |
|
195 | + Constants::PERMISSION_CREATE | |
|
196 | + Constants::PERMISSION_DELETE |
|
197 | + ), |
|
198 | + ]); |
|
199 | + } |
|
200 | + return $storage; |
|
201 | + }); |
|
202 | + |
|
203 | + Filesystem::logWarningWhenAddingStorageWrapper($prevLogging); |
|
204 | + } |
|
205 | + |
|
206 | + /** |
|
207 | + * Setup the full filesystem for the specified user |
|
208 | + */ |
|
209 | + public function setupForUser(IUser $user): void { |
|
210 | + if ($this->isSetupComplete($user)) { |
|
211 | + return; |
|
212 | + } |
|
213 | + $this->setupUsersComplete[] = $user->getUID(); |
|
214 | + |
|
215 | + $this->eventLogger->start('fs:setup:user:full', 'Setup full filesystem for user'); |
|
216 | + |
|
217 | + if (!isset($this->setupUserMountProviders[$user->getUID()])) { |
|
218 | + $this->setupUserMountProviders[$user->getUID()] = []; |
|
219 | + } |
|
220 | + |
|
221 | + $previouslySetupProviders = $this->setupUserMountProviders[$user->getUID()]; |
|
222 | + |
|
223 | + $this->setupForUserWith($user, function () use ($user) { |
|
224 | + $this->mountProviderCollection->addMountForUser($user, $this->mountManager, function ( |
|
225 | + IMountProvider $provider |
|
226 | + ) use ($user) { |
|
227 | + return !in_array(get_class($provider), $this->setupUserMountProviders[$user->getUID()]); |
|
228 | + }); |
|
229 | + }); |
|
230 | + $this->afterUserFullySetup($user, $previouslySetupProviders); |
|
231 | + $this->eventLogger->end('fs:setup:user:full'); |
|
232 | + } |
|
233 | + |
|
234 | + /** |
|
235 | + * part of the user setup that is run only once per user |
|
236 | + */ |
|
237 | + private function oneTimeUserSetup(IUser $user) { |
|
238 | + if ($this->isSetupStarted($user)) { |
|
239 | + return; |
|
240 | + } |
|
241 | + $this->setupUsers[] = $user->getUID(); |
|
242 | + |
|
243 | + $this->setupRoot(); |
|
244 | + |
|
245 | + $this->eventLogger->start('fs:setup:user:onetime', 'Onetime filesystem for user'); |
|
246 | + |
|
247 | + $this->setupBuiltinWrappers(); |
|
248 | + |
|
249 | + $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false); |
|
250 | + |
|
251 | + OC_Hook::emit('OC_Filesystem', 'preSetup', ['user' => $user->getUID()]); |
|
252 | + |
|
253 | + Filesystem::logWarningWhenAddingStorageWrapper($prevLogging); |
|
254 | + |
|
255 | + $userDir = '/' . $user->getUID() . '/files'; |
|
256 | + |
|
257 | + Filesystem::initInternal($userDir); |
|
258 | + |
|
259 | + if ($this->lockdownManager->canAccessFilesystem()) { |
|
260 | + $this->eventLogger->start('fs:setup:user:home', 'Setup home filesystem for user'); |
|
261 | + // home mounts are handled separate since we need to ensure this is mounted before we call the other mount providers |
|
262 | + $homeMount = $this->mountProviderCollection->getHomeMountForUser($user); |
|
263 | + $this->mountManager->addMount($homeMount); |
|
264 | + |
|
265 | + if ($homeMount->getStorageRootId() === -1) { |
|
266 | + $this->eventLogger->start('fs:setup:user:home:scan', 'Scan home filesystem for user'); |
|
267 | + $homeMount->getStorage()->mkdir(''); |
|
268 | + $homeMount->getStorage()->getScanner()->scan(''); |
|
269 | + $this->eventLogger->end('fs:setup:user:home:scan'); |
|
270 | + } |
|
271 | + $this->eventLogger->end('fs:setup:user:home'); |
|
272 | + } else { |
|
273 | + $this->mountManager->addMount(new MountPoint( |
|
274 | + new NullStorage([]), |
|
275 | + '/' . $user->getUID() |
|
276 | + )); |
|
277 | + $this->mountManager->addMount(new MountPoint( |
|
278 | + new NullStorage([]), |
|
279 | + '/' . $user->getUID() . '/files' |
|
280 | + )); |
|
281 | + $this->setupUsersComplete[] = $user->getUID(); |
|
282 | + } |
|
283 | + |
|
284 | + $this->listenForNewMountProviders(); |
|
285 | + |
|
286 | + $this->eventLogger->end('fs:setup:user:onetime'); |
|
287 | + } |
|
288 | + |
|
289 | + /** |
|
290 | + * Final housekeeping after a user has been fully setup |
|
291 | + */ |
|
292 | + private function afterUserFullySetup(IUser $user, array $previouslySetupProviders): void { |
|
293 | + $this->eventLogger->start('fs:setup:user:full:post', 'Housekeeping after user is setup'); |
|
294 | + $userRoot = '/' . $user->getUID() . '/'; |
|
295 | + $mounts = $this->mountManager->getAll(); |
|
296 | + $mounts = array_filter($mounts, function (IMountPoint $mount) use ($userRoot) { |
|
297 | + return strpos($mount->getMountPoint(), $userRoot) === 0; |
|
298 | + }); |
|
299 | + $allProviders = array_map(function (IMountProvider $provider) { |
|
300 | + return get_class($provider); |
|
301 | + }, $this->mountProviderCollection->getProviders()); |
|
302 | + $newProviders = array_diff($allProviders, $previouslySetupProviders); |
|
303 | + $mounts = array_filter($mounts, function (IMountPoint $mount) use ($previouslySetupProviders) { |
|
304 | + return !in_array($mount->getMountProvider(), $previouslySetupProviders); |
|
305 | + }); |
|
306 | + $this->userMountCache->registerMounts($user, $mounts, $newProviders); |
|
307 | + |
|
308 | + $cacheDuration = $this->config->getSystemValueInt('fs_mount_cache_duration', 5 * 60); |
|
309 | + if ($cacheDuration > 0) { |
|
310 | + $this->cache->set($user->getUID(), true, $cacheDuration); |
|
311 | + $this->fullSetupRequired[$user->getUID()] = false; |
|
312 | + } |
|
313 | + $this->eventLogger->end('fs:setup:user:full:post'); |
|
314 | + } |
|
315 | + |
|
316 | + /** |
|
317 | + * @param IUser $user |
|
318 | + * @param IMountPoint $mounts |
|
319 | + * @return void |
|
320 | + * @throws \OCP\HintException |
|
321 | + * @throws \OC\ServerNotAvailableException |
|
322 | + */ |
|
323 | + private function setupForUserWith(IUser $user, callable $mountCallback): void { |
|
324 | + $this->oneTimeUserSetup($user); |
|
325 | + |
|
326 | + if ($this->lockdownManager->canAccessFilesystem()) { |
|
327 | + $mountCallback(); |
|
328 | + } |
|
329 | + $this->eventLogger->start('fs:setup:user:post-init-mountpoint', 'post_initMountPoints legacy hook'); |
|
330 | + \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', ['user' => $user->getUID()]); |
|
331 | + $this->eventLogger->end('fs:setup:user:post-init-mountpoint'); |
|
332 | + |
|
333 | + $userDir = '/' . $user->getUID() . '/files'; |
|
334 | + $this->eventLogger->start('fs:setup:user:setup-hook', 'setup legacy hook'); |
|
335 | + OC_Hook::emit('OC_Filesystem', 'setup', ['user' => $user->getUID(), 'user_dir' => $userDir]); |
|
336 | + $this->eventLogger->end('fs:setup:user:setup-hook'); |
|
337 | + } |
|
338 | + |
|
339 | + /** |
|
340 | + * Set up the root filesystem |
|
341 | + */ |
|
342 | + public function setupRoot(): void { |
|
343 | + //setting up the filesystem twice can only lead to trouble |
|
344 | + if ($this->rootSetup) { |
|
345 | + return; |
|
346 | + } |
|
347 | + $this->rootSetup = true; |
|
348 | + |
|
349 | + $this->eventLogger->start('fs:setup:root', 'Setup root filesystem'); |
|
350 | + |
|
351 | + $this->setupBuiltinWrappers(); |
|
352 | + |
|
353 | + $rootMounts = $this->mountProviderCollection->getRootMounts(); |
|
354 | + foreach ($rootMounts as $rootMountProvider) { |
|
355 | + $this->mountManager->addMount($rootMountProvider); |
|
356 | + } |
|
357 | + |
|
358 | + $this->eventLogger->end('fs:setup:root'); |
|
359 | + } |
|
360 | + |
|
361 | + /** |
|
362 | + * Get the user to setup for a path or `null` if the root needs to be setup |
|
363 | + * |
|
364 | + * @param string $path |
|
365 | + * @return IUser|null |
|
366 | + */ |
|
367 | + private function getUserForPath(string $path) { |
|
368 | + if (strpos($path, '/__groupfolders') === 0) { |
|
369 | + return null; |
|
370 | + } elseif (substr_count($path, '/') < 2) { |
|
371 | + if ($user = $this->userSession->getUser()) { |
|
372 | + return $user; |
|
373 | + } else { |
|
374 | + return null; |
|
375 | + } |
|
376 | + } elseif (strpos($path, '/appdata_' . \OC_Util::getInstanceId()) === 0 || strpos($path, '/files_external/') === 0) { |
|
377 | + return null; |
|
378 | + } else { |
|
379 | + [, $userId] = explode('/', $path); |
|
380 | + } |
|
381 | + |
|
382 | + return $this->userManager->get($userId); |
|
383 | + } |
|
384 | + |
|
385 | + /** |
|
386 | + * Set up the filesystem for the specified path |
|
387 | + */ |
|
388 | + public function setupForPath(string $path, bool $includeChildren = false): void { |
|
389 | + $user = $this->getUserForPath($path); |
|
390 | + if (!$user) { |
|
391 | + $this->setupRoot(); |
|
392 | + return; |
|
393 | + } |
|
394 | + |
|
395 | + if ($this->isSetupComplete($user)) { |
|
396 | + return; |
|
397 | + } |
|
398 | + |
|
399 | + if ($this->fullSetupRequired($user)) { |
|
400 | + $this->setupForUser($user); |
|
401 | + return; |
|
402 | + } |
|
403 | + |
|
404 | + // for the user's home folder, and includes children we need everything always |
|
405 | + if (rtrim($path) === "/" . $user->getUID() . "/files" && $includeChildren) { |
|
406 | + $this->setupForUser($user); |
|
407 | + return; |
|
408 | + } |
|
409 | + |
|
410 | + if (!isset($this->setupUserMountProviders[$user->getUID()])) { |
|
411 | + $this->setupUserMountProviders[$user->getUID()] = []; |
|
412 | + } |
|
413 | + $setupProviders = &$this->setupUserMountProviders[$user->getUID()]; |
|
414 | + $currentProviders = []; |
|
415 | + |
|
416 | + try { |
|
417 | + $cachedMount = $this->userMountCache->getMountForPath($user, $path); |
|
418 | + } catch (NotFoundException $e) { |
|
419 | + $this->setupForUser($user); |
|
420 | + return; |
|
421 | + } |
|
422 | + |
|
423 | + $this->oneTimeUserSetup($user); |
|
424 | + |
|
425 | + $this->eventLogger->start('fs:setup:user:path', "Setup $path filesystem for user"); |
|
426 | + $this->eventLogger->start('fs:setup:user:path:find', "Find mountpoint for $path"); |
|
427 | + |
|
428 | + $mounts = []; |
|
429 | + if (!in_array($cachedMount->getMountProvider(), $setupProviders)) { |
|
430 | + $currentProviders[] = $cachedMount->getMountProvider(); |
|
431 | + if ($cachedMount->getMountProvider()) { |
|
432 | + $setupProviders[] = $cachedMount->getMountProvider(); |
|
433 | + $mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$cachedMount->getMountProvider()]); |
|
434 | + } else { |
|
435 | + $this->logger->debug("mount at " . $cachedMount->getMountPoint() . " has no provider set, performing full setup"); |
|
436 | + $this->eventLogger->end('fs:setup:user:path:find'); |
|
437 | + $this->setupForUser($user); |
|
438 | + $this->eventLogger->end('fs:setup:user:path'); |
|
439 | + return; |
|
440 | + } |
|
441 | + } |
|
442 | + |
|
443 | + if ($includeChildren) { |
|
444 | + $subCachedMounts = $this->userMountCache->getMountsInPath($user, $path); |
|
445 | + $this->eventLogger->end('fs:setup:user:path:find'); |
|
446 | + |
|
447 | + $needsFullSetup = array_reduce($subCachedMounts, function (bool $needsFullSetup, ICachedMountInfo $cachedMountInfo) { |
|
448 | + return $needsFullSetup || $cachedMountInfo->getMountProvider() === ''; |
|
449 | + }, false); |
|
450 | + |
|
451 | + if ($needsFullSetup) { |
|
452 | + $this->logger->debug("mount has no provider set, performing full setup"); |
|
453 | + $this->setupForUser($user); |
|
454 | + $this->eventLogger->end('fs:setup:user:path'); |
|
455 | + return; |
|
456 | + } else { |
|
457 | + foreach ($subCachedMounts as $cachedMount) { |
|
458 | + if (!in_array($cachedMount->getMountProvider(), $setupProviders)) { |
|
459 | + $currentProviders[] = $cachedMount->getMountProvider(); |
|
460 | + $setupProviders[] = $cachedMount->getMountProvider(); |
|
461 | + $mounts = array_merge($mounts, $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$cachedMount->getMountProvider()])); |
|
462 | + } |
|
463 | + } |
|
464 | + } |
|
465 | + } else { |
|
466 | + $this->eventLogger->end('fs:setup:user:path:find'); |
|
467 | + } |
|
468 | + |
|
469 | + if (count($mounts)) { |
|
470 | + $this->userMountCache->registerMounts($user, $mounts, $currentProviders); |
|
471 | + $this->setupForUserWith($user, function () use ($mounts) { |
|
472 | + array_walk($mounts, [$this->mountManager, 'addMount']); |
|
473 | + }); |
|
474 | + } elseif (!$this->isSetupStarted($user)) { |
|
475 | + $this->oneTimeUserSetup($user); |
|
476 | + } |
|
477 | + $this->eventLogger->end('fs:setup:user:path'); |
|
478 | + } |
|
479 | + |
|
480 | + private function fullSetupRequired(IUser $user): bool { |
|
481 | + // we perform a "cached" setup only after having done the full setup recently |
|
482 | + // this is also used to trigger a full setup after handling events that are likely |
|
483 | + // to change the available mounts |
|
484 | + if (!isset($this->fullSetupRequired[$user->getUID()])) { |
|
485 | + $this->fullSetupRequired[$user->getUID()] = !$this->cache->get($user->getUID()); |
|
486 | + } |
|
487 | + return $this->fullSetupRequired[$user->getUID()]; |
|
488 | + } |
|
489 | + |
|
490 | + /** |
|
491 | + * @param string $path |
|
492 | + * @param string[] $providers |
|
493 | + */ |
|
494 | + public function setupForProvider(string $path, array $providers): void { |
|
495 | + $user = $this->getUserForPath($path); |
|
496 | + if (!$user) { |
|
497 | + $this->setupRoot(); |
|
498 | + return; |
|
499 | + } |
|
500 | + |
|
501 | + if ($this->isSetupComplete($user)) { |
|
502 | + return; |
|
503 | + } |
|
504 | + |
|
505 | + if ($this->fullSetupRequired($user)) { |
|
506 | + $this->setupForUser($user); |
|
507 | + return; |
|
508 | + } |
|
509 | + |
|
510 | + $this->eventLogger->start('fs:setup:user:providers', "Setup filesystem for " . implode(', ', $providers)); |
|
511 | + |
|
512 | + $this->oneTimeUserSetup($user); |
|
513 | + |
|
514 | + // home providers are always used |
|
515 | + $providers = array_filter($providers, function (string $provider) { |
|
516 | + return !is_subclass_of($provider, IHomeMountProvider::class); |
|
517 | + }); |
|
518 | + |
|
519 | + if (in_array('', $providers)) { |
|
520 | + $this->setupForUser($user); |
|
521 | + return; |
|
522 | + } |
|
523 | + $setupProviders = $this->setupUserMountProviders[$user->getUID()] ?? []; |
|
524 | + |
|
525 | + $providers = array_diff($providers, $setupProviders); |
|
526 | + if (count($providers) === 0) { |
|
527 | + if (!$this->isSetupStarted($user)) { |
|
528 | + $this->oneTimeUserSetup($user); |
|
529 | + } |
|
530 | + $this->eventLogger->end('fs:setup:user:providers'); |
|
531 | + return; |
|
532 | + } else { |
|
533 | + $this->setupUserMountProviders[$user->getUID()] = array_merge($setupProviders, $providers); |
|
534 | + $mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, $providers); |
|
535 | + } |
|
536 | + |
|
537 | + $this->userMountCache->registerMounts($user, $mounts, $providers); |
|
538 | + $this->setupForUserWith($user, function () use ($mounts) { |
|
539 | + array_walk($mounts, [$this->mountManager, 'addMount']); |
|
540 | + }); |
|
541 | + $this->eventLogger->end('fs:setup:user:providers'); |
|
542 | + } |
|
543 | + |
|
544 | + public function tearDown() { |
|
545 | + $this->setupUsers = []; |
|
546 | + $this->setupUsersComplete = []; |
|
547 | + $this->setupUserMountProviders = []; |
|
548 | + $this->fullSetupRequired = []; |
|
549 | + $this->rootSetup = false; |
|
550 | + $this->mountManager->clear(); |
|
551 | + $this->eventDispatcher->dispatchTyped(new FilesystemTornDownEvent()); |
|
552 | + } |
|
553 | + |
|
554 | + /** |
|
555 | + * Get mounts from mount providers that are registered after setup |
|
556 | + */ |
|
557 | + private function listenForNewMountProviders() { |
|
558 | + if (!$this->listeningForProviders) { |
|
559 | + $this->listeningForProviders = true; |
|
560 | + $this->mountProviderCollection->listen('\OC\Files\Config', 'registerMountProvider', function ( |
|
561 | + IMountProvider $provider |
|
562 | + ) { |
|
563 | + foreach ($this->setupUsers as $userId) { |
|
564 | + $user = $this->userManager->get($userId); |
|
565 | + if ($user) { |
|
566 | + $mounts = $provider->getMountsForUser($user, Filesystem::getLoader()); |
|
567 | + array_walk($mounts, [$this->mountManager, 'addMount']); |
|
568 | + } |
|
569 | + } |
|
570 | + }); |
|
571 | + } |
|
572 | + } |
|
573 | + |
|
574 | + private function setupListeners() { |
|
575 | + // note that this event handling is intentionally pessimistic |
|
576 | + // clearing the cache to often is better than not enough |
|
577 | + |
|
578 | + $this->eventDispatcher->addListener(UserAddedEvent::class, function (UserAddedEvent $event) { |
|
579 | + $this->cache->remove($event->getUser()->getUID()); |
|
580 | + }); |
|
581 | + $this->eventDispatcher->addListener(UserRemovedEvent::class, function (UserRemovedEvent $event) { |
|
582 | + $this->cache->remove($event->getUser()->getUID()); |
|
583 | + }); |
|
584 | + $this->eventDispatcher->addListener(ShareCreatedEvent::class, function (ShareCreatedEvent $event) { |
|
585 | + $this->cache->remove($event->getShare()->getSharedWith()); |
|
586 | + }); |
|
587 | + $this->eventDispatcher->addListener(InvalidateMountCacheEvent::class, function (InvalidateMountCacheEvent $event |
|
588 | + ) { |
|
589 | + if ($user = $event->getUser()) { |
|
590 | + $this->cache->remove($user->getUID()); |
|
591 | + } else { |
|
592 | + $this->cache->clear(); |
|
593 | + } |
|
594 | + }); |
|
595 | + |
|
596 | + $genericEvents = [ |
|
597 | + 'OCA\Circles\Events\CreatingCircleEvent', |
|
598 | + 'OCA\Circles\Events\DestroyingCircleEvent', |
|
599 | + 'OCA\Circles\Events\AddingCircleMemberEvent', |
|
600 | + 'OCA\Circles\Events\RemovingCircleMemberEvent', |
|
601 | + ]; |
|
602 | + |
|
603 | + foreach ($genericEvents as $genericEvent) { |
|
604 | + $this->eventDispatcher->addListener($genericEvent, function ($event) { |
|
605 | + $this->cache->clear(); |
|
606 | + }); |
|
607 | + } |
|
608 | + } |
|
609 | 609 | } |
@@ -132,14 +132,14 @@ discard block |
||
132 | 132 | OC_App::loadApps(['filesystem']); |
133 | 133 | $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false); |
134 | 134 | |
135 | - Filesystem::addStorageWrapper('mount_options', function ($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
135 | + Filesystem::addStorageWrapper('mount_options', function($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
136 | 136 | if ($storage->instanceOfStorage(Common::class)) { |
137 | 137 | $storage->setMountOptions($mount->getOptions()); |
138 | 138 | } |
139 | 139 | return $storage; |
140 | 140 | }); |
141 | 141 | |
142 | - Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
142 | + Filesystem::addStorageWrapper('enable_sharing', function($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
143 | 143 | if (!$mount->getOption('enable_sharing', true)) { |
144 | 144 | return new PermissionsMask([ |
145 | 145 | 'storage' => $storage, |
@@ -150,21 +150,21 @@ discard block |
||
150 | 150 | }); |
151 | 151 | |
152 | 152 | // install storage availability wrapper, before most other wrappers |
153 | - Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, IStorage $storage) { |
|
153 | + Filesystem::addStorageWrapper('oc_availability', function($mountPoint, IStorage $storage) { |
|
154 | 154 | if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) { |
155 | 155 | return new Availability(['storage' => $storage]); |
156 | 156 | } |
157 | 157 | return $storage; |
158 | 158 | }); |
159 | 159 | |
160 | - Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
160 | + Filesystem::addStorageWrapper('oc_encoding', function($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
161 | 161 | if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) { |
162 | 162 | return new Encoding(['storage' => $storage]); |
163 | 163 | } |
164 | 164 | return $storage; |
165 | 165 | }); |
166 | 166 | |
167 | - Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) { |
|
167 | + Filesystem::addStorageWrapper('oc_quota', function($mountPoint, $storage) { |
|
168 | 168 | // set up quota for home storages, even for other users |
169 | 169 | // which can happen when using sharing |
170 | 170 | |
@@ -174,7 +174,7 @@ discard block |
||
174 | 174 | if ($storage->instanceOfStorage(HomeObjectStoreStorage::class) || $storage->instanceOfStorage(Home::class)) { |
175 | 175 | if (is_object($storage->getUser())) { |
176 | 176 | $user = $storage->getUser(); |
177 | - return new Quota(['storage' => $storage, 'quotaCallback' => function () use ($user) { |
|
177 | + return new Quota(['storage' => $storage, 'quotaCallback' => function() use ($user) { |
|
178 | 178 | return OC_Util::getUserQuota($user); |
179 | 179 | }, 'root' => 'files']); |
180 | 180 | } |
@@ -183,7 +183,7 @@ discard block |
||
183 | 183 | return $storage; |
184 | 184 | }); |
185 | 185 | |
186 | - Filesystem::addStorageWrapper('readonly', function ($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
186 | + Filesystem::addStorageWrapper('readonly', function($mountPoint, IStorage $storage, IMountPoint $mount) { |
|
187 | 187 | /* |
188 | 188 | * Do not allow any operations that modify the storage |
189 | 189 | */ |
@@ -220,8 +220,8 @@ discard block |
||
220 | 220 | |
221 | 221 | $previouslySetupProviders = $this->setupUserMountProviders[$user->getUID()]; |
222 | 222 | |
223 | - $this->setupForUserWith($user, function () use ($user) { |
|
224 | - $this->mountProviderCollection->addMountForUser($user, $this->mountManager, function ( |
|
223 | + $this->setupForUserWith($user, function() use ($user) { |
|
224 | + $this->mountProviderCollection->addMountForUser($user, $this->mountManager, function( |
|
225 | 225 | IMountProvider $provider |
226 | 226 | ) use ($user) { |
227 | 227 | return !in_array(get_class($provider), $this->setupUserMountProviders[$user->getUID()]); |
@@ -252,7 +252,7 @@ discard block |
||
252 | 252 | |
253 | 253 | Filesystem::logWarningWhenAddingStorageWrapper($prevLogging); |
254 | 254 | |
255 | - $userDir = '/' . $user->getUID() . '/files'; |
|
255 | + $userDir = '/'.$user->getUID().'/files'; |
|
256 | 256 | |
257 | 257 | Filesystem::initInternal($userDir); |
258 | 258 | |
@@ -272,11 +272,11 @@ discard block |
||
272 | 272 | } else { |
273 | 273 | $this->mountManager->addMount(new MountPoint( |
274 | 274 | new NullStorage([]), |
275 | - '/' . $user->getUID() |
|
275 | + '/'.$user->getUID() |
|
276 | 276 | )); |
277 | 277 | $this->mountManager->addMount(new MountPoint( |
278 | 278 | new NullStorage([]), |
279 | - '/' . $user->getUID() . '/files' |
|
279 | + '/'.$user->getUID().'/files' |
|
280 | 280 | )); |
281 | 281 | $this->setupUsersComplete[] = $user->getUID(); |
282 | 282 | } |
@@ -291,16 +291,16 @@ discard block |
||
291 | 291 | */ |
292 | 292 | private function afterUserFullySetup(IUser $user, array $previouslySetupProviders): void { |
293 | 293 | $this->eventLogger->start('fs:setup:user:full:post', 'Housekeeping after user is setup'); |
294 | - $userRoot = '/' . $user->getUID() . '/'; |
|
294 | + $userRoot = '/'.$user->getUID().'/'; |
|
295 | 295 | $mounts = $this->mountManager->getAll(); |
296 | - $mounts = array_filter($mounts, function (IMountPoint $mount) use ($userRoot) { |
|
296 | + $mounts = array_filter($mounts, function(IMountPoint $mount) use ($userRoot) { |
|
297 | 297 | return strpos($mount->getMountPoint(), $userRoot) === 0; |
298 | 298 | }); |
299 | - $allProviders = array_map(function (IMountProvider $provider) { |
|
299 | + $allProviders = array_map(function(IMountProvider $provider) { |
|
300 | 300 | return get_class($provider); |
301 | 301 | }, $this->mountProviderCollection->getProviders()); |
302 | 302 | $newProviders = array_diff($allProviders, $previouslySetupProviders); |
303 | - $mounts = array_filter($mounts, function (IMountPoint $mount) use ($previouslySetupProviders) { |
|
303 | + $mounts = array_filter($mounts, function(IMountPoint $mount) use ($previouslySetupProviders) { |
|
304 | 304 | return !in_array($mount->getMountProvider(), $previouslySetupProviders); |
305 | 305 | }); |
306 | 306 | $this->userMountCache->registerMounts($user, $mounts, $newProviders); |
@@ -330,7 +330,7 @@ discard block |
||
330 | 330 | \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', ['user' => $user->getUID()]); |
331 | 331 | $this->eventLogger->end('fs:setup:user:post-init-mountpoint'); |
332 | 332 | |
333 | - $userDir = '/' . $user->getUID() . '/files'; |
|
333 | + $userDir = '/'.$user->getUID().'/files'; |
|
334 | 334 | $this->eventLogger->start('fs:setup:user:setup-hook', 'setup legacy hook'); |
335 | 335 | OC_Hook::emit('OC_Filesystem', 'setup', ['user' => $user->getUID(), 'user_dir' => $userDir]); |
336 | 336 | $this->eventLogger->end('fs:setup:user:setup-hook'); |
@@ -373,7 +373,7 @@ discard block |
||
373 | 373 | } else { |
374 | 374 | return null; |
375 | 375 | } |
376 | - } elseif (strpos($path, '/appdata_' . \OC_Util::getInstanceId()) === 0 || strpos($path, '/files_external/') === 0) { |
|
376 | + } elseif (strpos($path, '/appdata_'.\OC_Util::getInstanceId()) === 0 || strpos($path, '/files_external/') === 0) { |
|
377 | 377 | return null; |
378 | 378 | } else { |
379 | 379 | [, $userId] = explode('/', $path); |
@@ -402,7 +402,7 @@ discard block |
||
402 | 402 | } |
403 | 403 | |
404 | 404 | // for the user's home folder, and includes children we need everything always |
405 | - if (rtrim($path) === "/" . $user->getUID() . "/files" && $includeChildren) { |
|
405 | + if (rtrim($path) === "/".$user->getUID()."/files" && $includeChildren) { |
|
406 | 406 | $this->setupForUser($user); |
407 | 407 | return; |
408 | 408 | } |
@@ -432,7 +432,7 @@ discard block |
||
432 | 432 | $setupProviders[] = $cachedMount->getMountProvider(); |
433 | 433 | $mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$cachedMount->getMountProvider()]); |
434 | 434 | } else { |
435 | - $this->logger->debug("mount at " . $cachedMount->getMountPoint() . " has no provider set, performing full setup"); |
|
435 | + $this->logger->debug("mount at ".$cachedMount->getMountPoint()." has no provider set, performing full setup"); |
|
436 | 436 | $this->eventLogger->end('fs:setup:user:path:find'); |
437 | 437 | $this->setupForUser($user); |
438 | 438 | $this->eventLogger->end('fs:setup:user:path'); |
@@ -444,7 +444,7 @@ discard block |
||
444 | 444 | $subCachedMounts = $this->userMountCache->getMountsInPath($user, $path); |
445 | 445 | $this->eventLogger->end('fs:setup:user:path:find'); |
446 | 446 | |
447 | - $needsFullSetup = array_reduce($subCachedMounts, function (bool $needsFullSetup, ICachedMountInfo $cachedMountInfo) { |
|
447 | + $needsFullSetup = array_reduce($subCachedMounts, function(bool $needsFullSetup, ICachedMountInfo $cachedMountInfo) { |
|
448 | 448 | return $needsFullSetup || $cachedMountInfo->getMountProvider() === ''; |
449 | 449 | }, false); |
450 | 450 | |
@@ -468,7 +468,7 @@ discard block |
||
468 | 468 | |
469 | 469 | if (count($mounts)) { |
470 | 470 | $this->userMountCache->registerMounts($user, $mounts, $currentProviders); |
471 | - $this->setupForUserWith($user, function () use ($mounts) { |
|
471 | + $this->setupForUserWith($user, function() use ($mounts) { |
|
472 | 472 | array_walk($mounts, [$this->mountManager, 'addMount']); |
473 | 473 | }); |
474 | 474 | } elseif (!$this->isSetupStarted($user)) { |
@@ -507,12 +507,12 @@ discard block |
||
507 | 507 | return; |
508 | 508 | } |
509 | 509 | |
510 | - $this->eventLogger->start('fs:setup:user:providers', "Setup filesystem for " . implode(', ', $providers)); |
|
510 | + $this->eventLogger->start('fs:setup:user:providers', "Setup filesystem for ".implode(', ', $providers)); |
|
511 | 511 | |
512 | 512 | $this->oneTimeUserSetup($user); |
513 | 513 | |
514 | 514 | // home providers are always used |
515 | - $providers = array_filter($providers, function (string $provider) { |
|
515 | + $providers = array_filter($providers, function(string $provider) { |
|
516 | 516 | return !is_subclass_of($provider, IHomeMountProvider::class); |
517 | 517 | }); |
518 | 518 | |
@@ -535,7 +535,7 @@ discard block |
||
535 | 535 | } |
536 | 536 | |
537 | 537 | $this->userMountCache->registerMounts($user, $mounts, $providers); |
538 | - $this->setupForUserWith($user, function () use ($mounts) { |
|
538 | + $this->setupForUserWith($user, function() use ($mounts) { |
|
539 | 539 | array_walk($mounts, [$this->mountManager, 'addMount']); |
540 | 540 | }); |
541 | 541 | $this->eventLogger->end('fs:setup:user:providers'); |
@@ -557,7 +557,7 @@ discard block |
||
557 | 557 | private function listenForNewMountProviders() { |
558 | 558 | if (!$this->listeningForProviders) { |
559 | 559 | $this->listeningForProviders = true; |
560 | - $this->mountProviderCollection->listen('\OC\Files\Config', 'registerMountProvider', function ( |
|
560 | + $this->mountProviderCollection->listen('\OC\Files\Config', 'registerMountProvider', function( |
|
561 | 561 | IMountProvider $provider |
562 | 562 | ) { |
563 | 563 | foreach ($this->setupUsers as $userId) { |
@@ -575,16 +575,16 @@ discard block |
||
575 | 575 | // note that this event handling is intentionally pessimistic |
576 | 576 | // clearing the cache to often is better than not enough |
577 | 577 | |
578 | - $this->eventDispatcher->addListener(UserAddedEvent::class, function (UserAddedEvent $event) { |
|
578 | + $this->eventDispatcher->addListener(UserAddedEvent::class, function(UserAddedEvent $event) { |
|
579 | 579 | $this->cache->remove($event->getUser()->getUID()); |
580 | 580 | }); |
581 | - $this->eventDispatcher->addListener(UserRemovedEvent::class, function (UserRemovedEvent $event) { |
|
581 | + $this->eventDispatcher->addListener(UserRemovedEvent::class, function(UserRemovedEvent $event) { |
|
582 | 582 | $this->cache->remove($event->getUser()->getUID()); |
583 | 583 | }); |
584 | - $this->eventDispatcher->addListener(ShareCreatedEvent::class, function (ShareCreatedEvent $event) { |
|
584 | + $this->eventDispatcher->addListener(ShareCreatedEvent::class, function(ShareCreatedEvent $event) { |
|
585 | 585 | $this->cache->remove($event->getShare()->getSharedWith()); |
586 | 586 | }); |
587 | - $this->eventDispatcher->addListener(InvalidateMountCacheEvent::class, function (InvalidateMountCacheEvent $event |
|
587 | + $this->eventDispatcher->addListener(InvalidateMountCacheEvent::class, function(InvalidateMountCacheEvent $event |
|
588 | 588 | ) { |
589 | 589 | if ($user = $event->getUser()) { |
590 | 590 | $this->cache->remove($user->getUID()); |
@@ -601,7 +601,7 @@ discard block |
||
601 | 601 | ]; |
602 | 602 | |
603 | 603 | foreach ($genericEvents as $genericEvent) { |
604 | - $this->eventDispatcher->addListener($genericEvent, function ($event) { |
|
604 | + $this->eventDispatcher->addListener($genericEvent, function($event) { |
|
605 | 605 | $this->cache->clear(); |
606 | 606 | }); |
607 | 607 | } |