Completed
Push — master ( 065a97...5cc942 )
by Robin
41:56 queued 18:47
created
lib/private/Files/Node/Root.php 2 patches
Indentation   +480 added lines, -480 removed lines patch added patch discarded remove patch
@@ -51,484 +51,484 @@
 block discarded – undo
51 51
  * @package OC\Files\Node
52 52
  */
53 53
 class Root extends Folder implements IRootFolder {
54
-	private Manager $mountManager;
55
-	private PublicEmitter $emitter;
56
-	private ?IUser $user;
57
-	private CappedMemoryCache $userFolderCache;
58
-	private IUserMountCache $userMountCache;
59
-	private LoggerInterface $logger;
60
-	private IUserManager $userManager;
61
-	private IEventDispatcher $eventDispatcher;
62
-	private ICache $pathByIdCache;
63
-
64
-	/**
65
-	 * @param Manager $manager
66
-	 * @param View $view
67
-	 * @param IUser|null $user
68
-	 */
69
-	public function __construct(
70
-		$manager,
71
-		$view,
72
-		$user,
73
-		IUserMountCache $userMountCache,
74
-		LoggerInterface $logger,
75
-		IUserManager $userManager,
76
-		IEventDispatcher $eventDispatcher,
77
-		ICacheFactory $cacheFactory,
78
-	) {
79
-		parent::__construct($this, $view, '');
80
-		$this->mountManager = $manager;
81
-		$this->user = $user;
82
-		$this->emitter = new PublicEmitter();
83
-		$this->userFolderCache = new CappedMemoryCache();
84
-		$this->userMountCache = $userMountCache;
85
-		$this->logger = $logger;
86
-		$this->userManager = $userManager;
87
-		$eventDispatcher->addListener(FilesystemTornDownEvent::class, function () {
88
-			$this->userFolderCache = new CappedMemoryCache();
89
-		});
90
-		$this->pathByIdCache = $cacheFactory->createLocal('path-by-id');
91
-	}
92
-
93
-	/**
94
-	 * Get the user for which the filesystem is setup
95
-	 *
96
-	 * @return \OC\User\User
97
-	 */
98
-	public function getUser() {
99
-		return $this->user;
100
-	}
101
-
102
-	/**
103
-	 * @param string $scope
104
-	 * @param string $method
105
-	 * @param callable $callback
106
-	 */
107
-	public function listen($scope, $method, callable $callback) {
108
-		$this->emitter->listen($scope, $method, $callback);
109
-	}
110
-
111
-	/**
112
-	 * @param string $scope optional
113
-	 * @param string $method optional
114
-	 * @param callable $callback optional
115
-	 */
116
-	public function removeListener($scope = null, $method = null, ?callable $callback = null) {
117
-		$this->emitter->removeListener($scope, $method, $callback);
118
-	}
119
-
120
-	/**
121
-	 * @param string $scope
122
-	 * @param string $method
123
-	 * @param Node[] $arguments
124
-	 */
125
-	public function emit($scope, $method, $arguments = []) {
126
-		$this->emitter->emit($scope, $method, $arguments);
127
-	}
128
-
129
-	/**
130
-	 * @param \OC\Files\Storage\Storage $storage
131
-	 * @param string $mountPoint
132
-	 * @param array $arguments
133
-	 */
134
-	public function mount($storage, $mountPoint, $arguments = []) {
135
-		$mount = new MountPoint($storage, $mountPoint, $arguments);
136
-		$this->mountManager->addMount($mount);
137
-	}
138
-
139
-	public function getMount(string $mountPoint): IMountPoint {
140
-		return $this->mountManager->find($mountPoint);
141
-	}
142
-
143
-	/**
144
-	 * @param string $mountPoint
145
-	 * @return \OC\Files\Mount\MountPoint[]
146
-	 */
147
-	public function getMountsIn(string $mountPoint): array {
148
-		return $this->mountManager->findIn($mountPoint);
149
-	}
150
-
151
-	/**
152
-	 * @param string $storageId
153
-	 * @return \OC\Files\Mount\MountPoint[]
154
-	 */
155
-	public function getMountByStorageId($storageId) {
156
-		return $this->mountManager->findByStorageId($storageId);
157
-	}
158
-
159
-	/**
160
-	 * @param int $numericId
161
-	 * @return MountPoint[]
162
-	 */
163
-	public function getMountByNumericStorageId($numericId) {
164
-		return $this->mountManager->findByNumericId($numericId);
165
-	}
166
-
167
-	/**
168
-	 * @param \OC\Files\Mount\MountPoint $mount
169
-	 */
170
-	public function unMount($mount) {
171
-		$this->mountManager->remove($mount);
172
-	}
173
-
174
-	public function get($path) {
175
-		$path = $this->normalizePath($path);
176
-		if ($this->isValidPath($path)) {
177
-			$fullPath = $this->getFullPath($path);
178
-			$fileInfo = $this->view->getFileInfo($fullPath, false);
179
-			if ($fileInfo) {
180
-				return $this->createNode($fullPath, $fileInfo, false);
181
-			} else {
182
-				throw new NotFoundException($path);
183
-			}
184
-		} else {
185
-			throw new NotPermittedException();
186
-		}
187
-	}
188
-
189
-	//most operations can't be done on the root
190
-
191
-	/**
192
-	 * @param string $targetPath
193
-	 * @return Node
194
-	 * @throws \OCP\Files\NotPermittedException
195
-	 */
196
-	public function rename($targetPath) {
197
-		throw new NotPermittedException();
198
-	}
199
-
200
-	public function delete() {
201
-		throw new NotPermittedException();
202
-	}
203
-
204
-	/**
205
-	 * @param string $targetPath
206
-	 * @return Node
207
-	 * @throws \OCP\Files\NotPermittedException
208
-	 */
209
-	public function copy($targetPath) {
210
-		throw new NotPermittedException();
211
-	}
212
-
213
-	/**
214
-	 * @param int $mtime
215
-	 * @throws \OCP\Files\NotPermittedException
216
-	 */
217
-	public function touch($mtime = null) {
218
-		throw new NotPermittedException();
219
-	}
220
-
221
-	/**
222
-	 * @return \OC\Files\Storage\Storage
223
-	 * @throws \OCP\Files\NotFoundException
224
-	 */
225
-	public function getStorage() {
226
-		throw new NotFoundException();
227
-	}
228
-
229
-	/**
230
-	 * @return string
231
-	 */
232
-	public function getPath() {
233
-		return '/';
234
-	}
235
-
236
-	/**
237
-	 * @return string
238
-	 */
239
-	public function getInternalPath() {
240
-		return '';
241
-	}
242
-
243
-	/**
244
-	 * @return int
245
-	 */
246
-	public function getId() {
247
-		return 0;
248
-	}
249
-
250
-	/**
251
-	 * @return array
252
-	 */
253
-	public function stat() {
254
-		return [];
255
-	}
256
-
257
-	/**
258
-	 * @return int
259
-	 */
260
-	public function getMTime() {
261
-		return 0;
262
-	}
263
-
264
-	/**
265
-	 * @param bool $includeMounts
266
-	 * @return int|float
267
-	 */
268
-	public function getSize($includeMounts = true): int|float {
269
-		return 0;
270
-	}
271
-
272
-	/**
273
-	 * @return string
274
-	 */
275
-	public function getEtag() {
276
-		return '';
277
-	}
278
-
279
-	/**
280
-	 * @return int
281
-	 */
282
-	public function getPermissions() {
283
-		return \OCP\Constants::PERMISSION_CREATE;
284
-	}
285
-
286
-	/**
287
-	 * @return bool
288
-	 */
289
-	public function isReadable() {
290
-		return false;
291
-	}
292
-
293
-	/**
294
-	 * @return bool
295
-	 */
296
-	public function isUpdateable() {
297
-		return false;
298
-	}
299
-
300
-	/**
301
-	 * @return bool
302
-	 */
303
-	public function isDeletable() {
304
-		return false;
305
-	}
306
-
307
-	/**
308
-	 * @return bool
309
-	 */
310
-	public function isShareable() {
311
-		return false;
312
-	}
313
-
314
-	/**
315
-	 * @throws \OCP\Files\NotFoundException
316
-	 */
317
-	public function getParent(): INode|IRootFolder {
318
-		throw new NotFoundException();
319
-	}
320
-
321
-	/**
322
-	 * @return string
323
-	 */
324
-	public function getName() {
325
-		return '';
326
-	}
327
-
328
-	/**
329
-	 * Returns a view to user's files folder
330
-	 *
331
-	 * @param string $userId user ID
332
-	 * @return \OCP\Files\Folder
333
-	 * @throws NoUserException
334
-	 * @throws NotPermittedException
335
-	 */
336
-	public function getUserFolder($userId) {
337
-		$userObject = $this->userManager->get($userId);
338
-
339
-		if (is_null($userObject)) {
340
-			$e = new NoUserException('Backends provided no user object');
341
-			$this->logger->error(
342
-				sprintf(
343
-					'Backends provided no user object for %s',
344
-					$userId
345
-				),
346
-				[
347
-					'app' => 'files',
348
-					'exception' => $e,
349
-				]
350
-			);
351
-			throw $e;
352
-		}
353
-
354
-		$userId = $userObject->getUID();
355
-
356
-		if (!$this->userFolderCache->hasKey($userId)) {
357
-			if ($this->mountManager->getSetupManager()->isSetupComplete($userObject)) {
358
-				try {
359
-					$folder = $this->get('/' . $userId . '/files');
360
-					if (!$folder instanceof \OCP\Files\Folder) {
361
-						throw new \Exception("Account folder for \"$userId\" exists as a file");
362
-					}
363
-				} catch (NotFoundException $e) {
364
-					if (!$this->nodeExists('/' . $userId)) {
365
-						$this->newFolder('/' . $userId);
366
-					}
367
-					$folder = $this->newFolder('/' . $userId . '/files');
368
-				}
369
-			} else {
370
-				$folder = new LazyUserFolder($this, $userObject, $this->mountManager);
371
-			}
372
-
373
-			$this->userFolderCache->set($userId, $folder);
374
-		}
375
-
376
-		return $this->userFolderCache->get($userId);
377
-	}
378
-
379
-	public function getUserMountCache() {
380
-		return $this->userMountCache;
381
-	}
382
-
383
-	public function getFirstNodeByIdInPath(int $id, string $path): ?INode {
384
-		// scope the cache by user, so we don't return nodes for different users
385
-		if ($this->user) {
386
-			$cachedPath = $this->pathByIdCache->get($this->user->getUID() . '::' . $id);
387
-			if ($cachedPath && str_starts_with($cachedPath, $path)) {
388
-				// getting the node by path is significantly cheaper than finding it by id
389
-				try {
390
-					$node = $this->get($cachedPath);
391
-					// by validating that the cached path still has the requested fileid we can work around the need to invalidate the cached path
392
-					// if the cached path is invalid or a different file now we fall back to the uncached logic
393
-					if ($node && $node->getId() === $id) {
394
-						return $node;
395
-					}
396
-				} catch (NotFoundException|NotPermittedException) {
397
-					// The file may be moved but the old path still in cache
398
-				}
399
-			}
400
-		}
401
-		$node = current($this->getByIdInPath($id, $path));
402
-		if (!$node) {
403
-			return null;
404
-		}
405
-
406
-		if ($this->user) {
407
-			$this->pathByIdCache->set($this->user->getUID() . '::' . $id, $node->getPath());
408
-		}
409
-		return $node;
410
-	}
411
-
412
-	/**
413
-	 * @param int $id
414
-	 * @return Node[]
415
-	 */
416
-	public function getByIdInPath(int $id, string $path): array {
417
-		$mountCache = $this->getUserMountCache();
418
-		if (strpos($path, '/', 1) > 0) {
419
-			[, $user] = explode('/', $path);
420
-		} else {
421
-			$user = null;
422
-		}
423
-		$mountsContainingFile = $mountCache->getMountsForFileId($id, $user);
424
-
425
-		// if the mount isn't in the cache yet, perform a setup first, then try again
426
-		if (count($mountsContainingFile) === 0) {
427
-			$this->mountManager->getSetupManager()->setupForPath($path, true);
428
-			$mountsContainingFile = $mountCache->getMountsForFileId($id, $user);
429
-		}
430
-
431
-		// when a user has access through the same storage through multiple paths
432
-		// (such as an external storage that is both mounted for a user and shared to the user)
433
-		// the mount cache will only hold a single entry for the storage
434
-		// this can lead to issues as the different ways the user has access to a storage can have different permissions
435
-		//
436
-		// so instead of using the cached entries directly, we instead filter the current mounts by the rootid of the cache entry
437
-
438
-		$mountRootIds = array_map(function ($mount) {
439
-			return $mount->getRootId();
440
-		}, $mountsContainingFile);
441
-		$mountRootPaths = array_map(function ($mount) {
442
-			return $mount->getRootInternalPath();
443
-		}, $mountsContainingFile);
444
-		$mountProviders = array_unique(array_map(function ($mount) {
445
-			return $mount->getMountProvider();
446
-		}, $mountsContainingFile));
447
-		$mountRoots = array_combine($mountRootIds, $mountRootPaths);
448
-
449
-		$mounts = $this->mountManager->getMountsByMountProvider($path, $mountProviders);
450
-
451
-		$mountsContainingFile = array_filter($mounts, function ($mount) use ($mountRoots) {
452
-			return isset($mountRoots[$mount->getStorageRootId()]);
453
-		});
454
-
455
-		if (count($mountsContainingFile) === 0) {
456
-			if ($user === $this->getAppDataDirectoryName()) {
457
-				$folder = $this->get($path);
458
-				if ($folder instanceof Folder) {
459
-					return $folder->getByIdInRootMount($id);
460
-				} else {
461
-					throw new \Exception('getByIdInPath with non folder');
462
-				}
463
-			}
464
-			return [];
465
-		}
466
-
467
-		$nodes = array_map(function (IMountPoint $mount) use ($id, $mountRoots) {
468
-			$rootInternalPath = $mountRoots[$mount->getStorageRootId()];
469
-			$cacheEntry = $mount->getStorage()->getCache()->get($id);
470
-			if (!$cacheEntry) {
471
-				return null;
472
-			}
473
-
474
-			// cache jails will hide the "true" internal path
475
-			$internalPath = ltrim($rootInternalPath . '/' . $cacheEntry->getPath(), '/');
476
-			$pathRelativeToMount = substr($internalPath, strlen($rootInternalPath));
477
-			$pathRelativeToMount = ltrim($pathRelativeToMount, '/');
478
-			$absolutePath = rtrim($mount->getMountPoint() . $pathRelativeToMount, '/');
479
-			$storage = $mount->getStorage();
480
-			if ($storage === null) {
481
-				return null;
482
-			}
483
-			$ownerId = $storage->getOwner($pathRelativeToMount);
484
-			if ($ownerId !== false) {
485
-				$owner = Server::get(IUserManager::class)->get($ownerId);
486
-			} else {
487
-				$owner = null;
488
-			}
489
-			return $this->createNode($absolutePath, new FileInfo(
490
-				$absolutePath,
491
-				$storage,
492
-				$cacheEntry->getPath(),
493
-				$cacheEntry,
494
-				$mount,
495
-				$owner,
496
-			));
497
-		}, $mountsContainingFile);
498
-
499
-		$nodes = array_filter($nodes);
500
-
501
-		$folders = array_filter($nodes, function (Node $node) use ($path) {
502
-			return PathHelper::getRelativePath($path, $node->getPath()) !== null;
503
-		});
504
-		usort($folders, function ($a, $b) {
505
-			return $b->getPath() <=> $a->getPath();
506
-		});
507
-		return $folders;
508
-	}
509
-
510
-	public function getNodeFromCacheEntryAndMount(ICacheEntry $cacheEntry, IMountPoint $mountPoint): INode {
511
-		$path = $cacheEntry->getPath();
512
-		$fullPath = $mountPoint->getMountPoint() . $path;
513
-		// todo: LazyNode?
514
-		$info = new FileInfo($fullPath, $mountPoint->getStorage(), $path, $cacheEntry, $mountPoint);
515
-		$parentPath = dirname($fullPath);
516
-		$parent = new LazyFolder($this, function () use ($parentPath) {
517
-			$parent = $this->get($parentPath);
518
-			if ($parent instanceof \OCP\Files\Folder) {
519
-				return $parent;
520
-			} else {
521
-				throw new \Exception("parent $parentPath is not a folder");
522
-			}
523
-		}, [
524
-			'path' => $parentPath,
525
-		]);
526
-		$isDir = $info->getType() === FileInfo::TYPE_FOLDER;
527
-		$view = new View('');
528
-		if ($isDir) {
529
-			return new Folder($this, $view, $fullPath, $info, $parent);
530
-		} else {
531
-			return new File($this, $view, $fullPath, $info, $parent);
532
-		}
533
-	}
54
+    private Manager $mountManager;
55
+    private PublicEmitter $emitter;
56
+    private ?IUser $user;
57
+    private CappedMemoryCache $userFolderCache;
58
+    private IUserMountCache $userMountCache;
59
+    private LoggerInterface $logger;
60
+    private IUserManager $userManager;
61
+    private IEventDispatcher $eventDispatcher;
62
+    private ICache $pathByIdCache;
63
+
64
+    /**
65
+     * @param Manager $manager
66
+     * @param View $view
67
+     * @param IUser|null $user
68
+     */
69
+    public function __construct(
70
+        $manager,
71
+        $view,
72
+        $user,
73
+        IUserMountCache $userMountCache,
74
+        LoggerInterface $logger,
75
+        IUserManager $userManager,
76
+        IEventDispatcher $eventDispatcher,
77
+        ICacheFactory $cacheFactory,
78
+    ) {
79
+        parent::__construct($this, $view, '');
80
+        $this->mountManager = $manager;
81
+        $this->user = $user;
82
+        $this->emitter = new PublicEmitter();
83
+        $this->userFolderCache = new CappedMemoryCache();
84
+        $this->userMountCache = $userMountCache;
85
+        $this->logger = $logger;
86
+        $this->userManager = $userManager;
87
+        $eventDispatcher->addListener(FilesystemTornDownEvent::class, function () {
88
+            $this->userFolderCache = new CappedMemoryCache();
89
+        });
90
+        $this->pathByIdCache = $cacheFactory->createLocal('path-by-id');
91
+    }
92
+
93
+    /**
94
+     * Get the user for which the filesystem is setup
95
+     *
96
+     * @return \OC\User\User
97
+     */
98
+    public function getUser() {
99
+        return $this->user;
100
+    }
101
+
102
+    /**
103
+     * @param string $scope
104
+     * @param string $method
105
+     * @param callable $callback
106
+     */
107
+    public function listen($scope, $method, callable $callback) {
108
+        $this->emitter->listen($scope, $method, $callback);
109
+    }
110
+
111
+    /**
112
+     * @param string $scope optional
113
+     * @param string $method optional
114
+     * @param callable $callback optional
115
+     */
116
+    public function removeListener($scope = null, $method = null, ?callable $callback = null) {
117
+        $this->emitter->removeListener($scope, $method, $callback);
118
+    }
119
+
120
+    /**
121
+     * @param string $scope
122
+     * @param string $method
123
+     * @param Node[] $arguments
124
+     */
125
+    public function emit($scope, $method, $arguments = []) {
126
+        $this->emitter->emit($scope, $method, $arguments);
127
+    }
128
+
129
+    /**
130
+     * @param \OC\Files\Storage\Storage $storage
131
+     * @param string $mountPoint
132
+     * @param array $arguments
133
+     */
134
+    public function mount($storage, $mountPoint, $arguments = []) {
135
+        $mount = new MountPoint($storage, $mountPoint, $arguments);
136
+        $this->mountManager->addMount($mount);
137
+    }
138
+
139
+    public function getMount(string $mountPoint): IMountPoint {
140
+        return $this->mountManager->find($mountPoint);
141
+    }
142
+
143
+    /**
144
+     * @param string $mountPoint
145
+     * @return \OC\Files\Mount\MountPoint[]
146
+     */
147
+    public function getMountsIn(string $mountPoint): array {
148
+        return $this->mountManager->findIn($mountPoint);
149
+    }
150
+
151
+    /**
152
+     * @param string $storageId
153
+     * @return \OC\Files\Mount\MountPoint[]
154
+     */
155
+    public function getMountByStorageId($storageId) {
156
+        return $this->mountManager->findByStorageId($storageId);
157
+    }
158
+
159
+    /**
160
+     * @param int $numericId
161
+     * @return MountPoint[]
162
+     */
163
+    public function getMountByNumericStorageId($numericId) {
164
+        return $this->mountManager->findByNumericId($numericId);
165
+    }
166
+
167
+    /**
168
+     * @param \OC\Files\Mount\MountPoint $mount
169
+     */
170
+    public function unMount($mount) {
171
+        $this->mountManager->remove($mount);
172
+    }
173
+
174
+    public function get($path) {
175
+        $path = $this->normalizePath($path);
176
+        if ($this->isValidPath($path)) {
177
+            $fullPath = $this->getFullPath($path);
178
+            $fileInfo = $this->view->getFileInfo($fullPath, false);
179
+            if ($fileInfo) {
180
+                return $this->createNode($fullPath, $fileInfo, false);
181
+            } else {
182
+                throw new NotFoundException($path);
183
+            }
184
+        } else {
185
+            throw new NotPermittedException();
186
+        }
187
+    }
188
+
189
+    //most operations can't be done on the root
190
+
191
+    /**
192
+     * @param string $targetPath
193
+     * @return Node
194
+     * @throws \OCP\Files\NotPermittedException
195
+     */
196
+    public function rename($targetPath) {
197
+        throw new NotPermittedException();
198
+    }
199
+
200
+    public function delete() {
201
+        throw new NotPermittedException();
202
+    }
203
+
204
+    /**
205
+     * @param string $targetPath
206
+     * @return Node
207
+     * @throws \OCP\Files\NotPermittedException
208
+     */
209
+    public function copy($targetPath) {
210
+        throw new NotPermittedException();
211
+    }
212
+
213
+    /**
214
+     * @param int $mtime
215
+     * @throws \OCP\Files\NotPermittedException
216
+     */
217
+    public function touch($mtime = null) {
218
+        throw new NotPermittedException();
219
+    }
220
+
221
+    /**
222
+     * @return \OC\Files\Storage\Storage
223
+     * @throws \OCP\Files\NotFoundException
224
+     */
225
+    public function getStorage() {
226
+        throw new NotFoundException();
227
+    }
228
+
229
+    /**
230
+     * @return string
231
+     */
232
+    public function getPath() {
233
+        return '/';
234
+    }
235
+
236
+    /**
237
+     * @return string
238
+     */
239
+    public function getInternalPath() {
240
+        return '';
241
+    }
242
+
243
+    /**
244
+     * @return int
245
+     */
246
+    public function getId() {
247
+        return 0;
248
+    }
249
+
250
+    /**
251
+     * @return array
252
+     */
253
+    public function stat() {
254
+        return [];
255
+    }
256
+
257
+    /**
258
+     * @return int
259
+     */
260
+    public function getMTime() {
261
+        return 0;
262
+    }
263
+
264
+    /**
265
+     * @param bool $includeMounts
266
+     * @return int|float
267
+     */
268
+    public function getSize($includeMounts = true): int|float {
269
+        return 0;
270
+    }
271
+
272
+    /**
273
+     * @return string
274
+     */
275
+    public function getEtag() {
276
+        return '';
277
+    }
278
+
279
+    /**
280
+     * @return int
281
+     */
282
+    public function getPermissions() {
283
+        return \OCP\Constants::PERMISSION_CREATE;
284
+    }
285
+
286
+    /**
287
+     * @return bool
288
+     */
289
+    public function isReadable() {
290
+        return false;
291
+    }
292
+
293
+    /**
294
+     * @return bool
295
+     */
296
+    public function isUpdateable() {
297
+        return false;
298
+    }
299
+
300
+    /**
301
+     * @return bool
302
+     */
303
+    public function isDeletable() {
304
+        return false;
305
+    }
306
+
307
+    /**
308
+     * @return bool
309
+     */
310
+    public function isShareable() {
311
+        return false;
312
+    }
313
+
314
+    /**
315
+     * @throws \OCP\Files\NotFoundException
316
+     */
317
+    public function getParent(): INode|IRootFolder {
318
+        throw new NotFoundException();
319
+    }
320
+
321
+    /**
322
+     * @return string
323
+     */
324
+    public function getName() {
325
+        return '';
326
+    }
327
+
328
+    /**
329
+     * Returns a view to user's files folder
330
+     *
331
+     * @param string $userId user ID
332
+     * @return \OCP\Files\Folder
333
+     * @throws NoUserException
334
+     * @throws NotPermittedException
335
+     */
336
+    public function getUserFolder($userId) {
337
+        $userObject = $this->userManager->get($userId);
338
+
339
+        if (is_null($userObject)) {
340
+            $e = new NoUserException('Backends provided no user object');
341
+            $this->logger->error(
342
+                sprintf(
343
+                    'Backends provided no user object for %s',
344
+                    $userId
345
+                ),
346
+                [
347
+                    'app' => 'files',
348
+                    'exception' => $e,
349
+                ]
350
+            );
351
+            throw $e;
352
+        }
353
+
354
+        $userId = $userObject->getUID();
355
+
356
+        if (!$this->userFolderCache->hasKey($userId)) {
357
+            if ($this->mountManager->getSetupManager()->isSetupComplete($userObject)) {
358
+                try {
359
+                    $folder = $this->get('/' . $userId . '/files');
360
+                    if (!$folder instanceof \OCP\Files\Folder) {
361
+                        throw new \Exception("Account folder for \"$userId\" exists as a file");
362
+                    }
363
+                } catch (NotFoundException $e) {
364
+                    if (!$this->nodeExists('/' . $userId)) {
365
+                        $this->newFolder('/' . $userId);
366
+                    }
367
+                    $folder = $this->newFolder('/' . $userId . '/files');
368
+                }
369
+            } else {
370
+                $folder = new LazyUserFolder($this, $userObject, $this->mountManager);
371
+            }
372
+
373
+            $this->userFolderCache->set($userId, $folder);
374
+        }
375
+
376
+        return $this->userFolderCache->get($userId);
377
+    }
378
+
379
+    public function getUserMountCache() {
380
+        return $this->userMountCache;
381
+    }
382
+
383
+    public function getFirstNodeByIdInPath(int $id, string $path): ?INode {
384
+        // scope the cache by user, so we don't return nodes for different users
385
+        if ($this->user) {
386
+            $cachedPath = $this->pathByIdCache->get($this->user->getUID() . '::' . $id);
387
+            if ($cachedPath && str_starts_with($cachedPath, $path)) {
388
+                // getting the node by path is significantly cheaper than finding it by id
389
+                try {
390
+                    $node = $this->get($cachedPath);
391
+                    // by validating that the cached path still has the requested fileid we can work around the need to invalidate the cached path
392
+                    // if the cached path is invalid or a different file now we fall back to the uncached logic
393
+                    if ($node && $node->getId() === $id) {
394
+                        return $node;
395
+                    }
396
+                } catch (NotFoundException|NotPermittedException) {
397
+                    // The file may be moved but the old path still in cache
398
+                }
399
+            }
400
+        }
401
+        $node = current($this->getByIdInPath($id, $path));
402
+        if (!$node) {
403
+            return null;
404
+        }
405
+
406
+        if ($this->user) {
407
+            $this->pathByIdCache->set($this->user->getUID() . '::' . $id, $node->getPath());
408
+        }
409
+        return $node;
410
+    }
411
+
412
+    /**
413
+     * @param int $id
414
+     * @return Node[]
415
+     */
416
+    public function getByIdInPath(int $id, string $path): array {
417
+        $mountCache = $this->getUserMountCache();
418
+        if (strpos($path, '/', 1) > 0) {
419
+            [, $user] = explode('/', $path);
420
+        } else {
421
+            $user = null;
422
+        }
423
+        $mountsContainingFile = $mountCache->getMountsForFileId($id, $user);
424
+
425
+        // if the mount isn't in the cache yet, perform a setup first, then try again
426
+        if (count($mountsContainingFile) === 0) {
427
+            $this->mountManager->getSetupManager()->setupForPath($path, true);
428
+            $mountsContainingFile = $mountCache->getMountsForFileId($id, $user);
429
+        }
430
+
431
+        // when a user has access through the same storage through multiple paths
432
+        // (such as an external storage that is both mounted for a user and shared to the user)
433
+        // the mount cache will only hold a single entry for the storage
434
+        // this can lead to issues as the different ways the user has access to a storage can have different permissions
435
+        //
436
+        // so instead of using the cached entries directly, we instead filter the current mounts by the rootid of the cache entry
437
+
438
+        $mountRootIds = array_map(function ($mount) {
439
+            return $mount->getRootId();
440
+        }, $mountsContainingFile);
441
+        $mountRootPaths = array_map(function ($mount) {
442
+            return $mount->getRootInternalPath();
443
+        }, $mountsContainingFile);
444
+        $mountProviders = array_unique(array_map(function ($mount) {
445
+            return $mount->getMountProvider();
446
+        }, $mountsContainingFile));
447
+        $mountRoots = array_combine($mountRootIds, $mountRootPaths);
448
+
449
+        $mounts = $this->mountManager->getMountsByMountProvider($path, $mountProviders);
450
+
451
+        $mountsContainingFile = array_filter($mounts, function ($mount) use ($mountRoots) {
452
+            return isset($mountRoots[$mount->getStorageRootId()]);
453
+        });
454
+
455
+        if (count($mountsContainingFile) === 0) {
456
+            if ($user === $this->getAppDataDirectoryName()) {
457
+                $folder = $this->get($path);
458
+                if ($folder instanceof Folder) {
459
+                    return $folder->getByIdInRootMount($id);
460
+                } else {
461
+                    throw new \Exception('getByIdInPath with non folder');
462
+                }
463
+            }
464
+            return [];
465
+        }
466
+
467
+        $nodes = array_map(function (IMountPoint $mount) use ($id, $mountRoots) {
468
+            $rootInternalPath = $mountRoots[$mount->getStorageRootId()];
469
+            $cacheEntry = $mount->getStorage()->getCache()->get($id);
470
+            if (!$cacheEntry) {
471
+                return null;
472
+            }
473
+
474
+            // cache jails will hide the "true" internal path
475
+            $internalPath = ltrim($rootInternalPath . '/' . $cacheEntry->getPath(), '/');
476
+            $pathRelativeToMount = substr($internalPath, strlen($rootInternalPath));
477
+            $pathRelativeToMount = ltrim($pathRelativeToMount, '/');
478
+            $absolutePath = rtrim($mount->getMountPoint() . $pathRelativeToMount, '/');
479
+            $storage = $mount->getStorage();
480
+            if ($storage === null) {
481
+                return null;
482
+            }
483
+            $ownerId = $storage->getOwner($pathRelativeToMount);
484
+            if ($ownerId !== false) {
485
+                $owner = Server::get(IUserManager::class)->get($ownerId);
486
+            } else {
487
+                $owner = null;
488
+            }
489
+            return $this->createNode($absolutePath, new FileInfo(
490
+                $absolutePath,
491
+                $storage,
492
+                $cacheEntry->getPath(),
493
+                $cacheEntry,
494
+                $mount,
495
+                $owner,
496
+            ));
497
+        }, $mountsContainingFile);
498
+
499
+        $nodes = array_filter($nodes);
500
+
501
+        $folders = array_filter($nodes, function (Node $node) use ($path) {
502
+            return PathHelper::getRelativePath($path, $node->getPath()) !== null;
503
+        });
504
+        usort($folders, function ($a, $b) {
505
+            return $b->getPath() <=> $a->getPath();
506
+        });
507
+        return $folders;
508
+    }
509
+
510
+    public function getNodeFromCacheEntryAndMount(ICacheEntry $cacheEntry, IMountPoint $mountPoint): INode {
511
+        $path = $cacheEntry->getPath();
512
+        $fullPath = $mountPoint->getMountPoint() . $path;
513
+        // todo: LazyNode?
514
+        $info = new FileInfo($fullPath, $mountPoint->getStorage(), $path, $cacheEntry, $mountPoint);
515
+        $parentPath = dirname($fullPath);
516
+        $parent = new LazyFolder($this, function () use ($parentPath) {
517
+            $parent = $this->get($parentPath);
518
+            if ($parent instanceof \OCP\Files\Folder) {
519
+                return $parent;
520
+            } else {
521
+                throw new \Exception("parent $parentPath is not a folder");
522
+            }
523
+        }, [
524
+            'path' => $parentPath,
525
+        ]);
526
+        $isDir = $info->getType() === FileInfo::TYPE_FOLDER;
527
+        $view = new View('');
528
+        if ($isDir) {
529
+            return new Folder($this, $view, $fullPath, $info, $parent);
530
+        } else {
531
+            return new File($this, $view, $fullPath, $info, $parent);
532
+        }
533
+    }
534 534
 }
Please login to merge, or discard this patch.
Spacing   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -84,7 +84,7 @@  discard block
 block discarded – undo
84 84
 		$this->userMountCache = $userMountCache;
85 85
 		$this->logger = $logger;
86 86
 		$this->userManager = $userManager;
87
-		$eventDispatcher->addListener(FilesystemTornDownEvent::class, function () {
87
+		$eventDispatcher->addListener(FilesystemTornDownEvent::class, function() {
88 88
 			$this->userFolderCache = new CappedMemoryCache();
89 89
 		});
90 90
 		$this->pathByIdCache = $cacheFactory->createLocal('path-by-id');
@@ -265,7 +265,7 @@  discard block
 block discarded – undo
265 265
 	 * @param bool $includeMounts
266 266
 	 * @return int|float
267 267
 	 */
268
-	public function getSize($includeMounts = true): int|float {
268
+	public function getSize($includeMounts = true): int | float {
269 269
 		return 0;
270 270
 	}
271 271
 
@@ -314,7 +314,7 @@  discard block
 block discarded – undo
314 314
 	/**
315 315
 	 * @throws \OCP\Files\NotFoundException
316 316
 	 */
317
-	public function getParent(): INode|IRootFolder {
317
+	public function getParent(): INode | IRootFolder {
318 318
 		throw new NotFoundException();
319 319
 	}
320 320
 
@@ -356,15 +356,15 @@  discard block
 block discarded – undo
356 356
 		if (!$this->userFolderCache->hasKey($userId)) {
357 357
 			if ($this->mountManager->getSetupManager()->isSetupComplete($userObject)) {
358 358
 				try {
359
-					$folder = $this->get('/' . $userId . '/files');
359
+					$folder = $this->get('/'.$userId.'/files');
360 360
 					if (!$folder instanceof \OCP\Files\Folder) {
361 361
 						throw new \Exception("Account folder for \"$userId\" exists as a file");
362 362
 					}
363 363
 				} catch (NotFoundException $e) {
364
-					if (!$this->nodeExists('/' . $userId)) {
365
-						$this->newFolder('/' . $userId);
364
+					if (!$this->nodeExists('/'.$userId)) {
365
+						$this->newFolder('/'.$userId);
366 366
 					}
367
-					$folder = $this->newFolder('/' . $userId . '/files');
367
+					$folder = $this->newFolder('/'.$userId.'/files');
368 368
 				}
369 369
 			} else {
370 370
 				$folder = new LazyUserFolder($this, $userObject, $this->mountManager);
@@ -383,7 +383,7 @@  discard block
 block discarded – undo
383 383
 	public function getFirstNodeByIdInPath(int $id, string $path): ?INode {
384 384
 		// scope the cache by user, so we don't return nodes for different users
385 385
 		if ($this->user) {
386
-			$cachedPath = $this->pathByIdCache->get($this->user->getUID() . '::' . $id);
386
+			$cachedPath = $this->pathByIdCache->get($this->user->getUID().'::'.$id);
387 387
 			if ($cachedPath && str_starts_with($cachedPath, $path)) {
388 388
 				// getting the node by path is significantly cheaper than finding it by id
389 389
 				try {
@@ -393,7 +393,7 @@  discard block
 block discarded – undo
393 393
 					if ($node && $node->getId() === $id) {
394 394
 						return $node;
395 395
 					}
396
-				} catch (NotFoundException|NotPermittedException) {
396
+				} catch (NotFoundException | NotPermittedException) {
397 397
 					// The file may be moved but the old path still in cache
398 398
 				}
399 399
 			}
@@ -404,7 +404,7 @@  discard block
 block discarded – undo
404 404
 		}
405 405
 
406 406
 		if ($this->user) {
407
-			$this->pathByIdCache->set($this->user->getUID() . '::' . $id, $node->getPath());
407
+			$this->pathByIdCache->set($this->user->getUID().'::'.$id, $node->getPath());
408 408
 		}
409 409
 		return $node;
410 410
 	}
@@ -435,20 +435,20 @@  discard block
 block discarded – undo
435 435
 		//
436 436
 		// so instead of using the cached entries directly, we instead filter the current mounts by the rootid of the cache entry
437 437
 
438
-		$mountRootIds = array_map(function ($mount) {
438
+		$mountRootIds = array_map(function($mount) {
439 439
 			return $mount->getRootId();
440 440
 		}, $mountsContainingFile);
441
-		$mountRootPaths = array_map(function ($mount) {
441
+		$mountRootPaths = array_map(function($mount) {
442 442
 			return $mount->getRootInternalPath();
443 443
 		}, $mountsContainingFile);
444
-		$mountProviders = array_unique(array_map(function ($mount) {
444
+		$mountProviders = array_unique(array_map(function($mount) {
445 445
 			return $mount->getMountProvider();
446 446
 		}, $mountsContainingFile));
447 447
 		$mountRoots = array_combine($mountRootIds, $mountRootPaths);
448 448
 
449 449
 		$mounts = $this->mountManager->getMountsByMountProvider($path, $mountProviders);
450 450
 
451
-		$mountsContainingFile = array_filter($mounts, function ($mount) use ($mountRoots) {
451
+		$mountsContainingFile = array_filter($mounts, function($mount) use ($mountRoots) {
452 452
 			return isset($mountRoots[$mount->getStorageRootId()]);
453 453
 		});
454 454
 
@@ -464,7 +464,7 @@  discard block
 block discarded – undo
464 464
 			return [];
465 465
 		}
466 466
 
467
-		$nodes = array_map(function (IMountPoint $mount) use ($id, $mountRoots) {
467
+		$nodes = array_map(function(IMountPoint $mount) use ($id, $mountRoots) {
468 468
 			$rootInternalPath = $mountRoots[$mount->getStorageRootId()];
469 469
 			$cacheEntry = $mount->getStorage()->getCache()->get($id);
470 470
 			if (!$cacheEntry) {
@@ -472,10 +472,10 @@  discard block
 block discarded – undo
472 472
 			}
473 473
 
474 474
 			// cache jails will hide the "true" internal path
475
-			$internalPath = ltrim($rootInternalPath . '/' . $cacheEntry->getPath(), '/');
475
+			$internalPath = ltrim($rootInternalPath.'/'.$cacheEntry->getPath(), '/');
476 476
 			$pathRelativeToMount = substr($internalPath, strlen($rootInternalPath));
477 477
 			$pathRelativeToMount = ltrim($pathRelativeToMount, '/');
478
-			$absolutePath = rtrim($mount->getMountPoint() . $pathRelativeToMount, '/');
478
+			$absolutePath = rtrim($mount->getMountPoint().$pathRelativeToMount, '/');
479 479
 			$storage = $mount->getStorage();
480 480
 			if ($storage === null) {
481 481
 				return null;
@@ -498,10 +498,10 @@  discard block
 block discarded – undo
498 498
 
499 499
 		$nodes = array_filter($nodes);
500 500
 
501
-		$folders = array_filter($nodes, function (Node $node) use ($path) {
501
+		$folders = array_filter($nodes, function(Node $node) use ($path) {
502 502
 			return PathHelper::getRelativePath($path, $node->getPath()) !== null;
503 503
 		});
504
-		usort($folders, function ($a, $b) {
504
+		usort($folders, function($a, $b) {
505 505
 			return $b->getPath() <=> $a->getPath();
506 506
 		});
507 507
 		return $folders;
@@ -509,11 +509,11 @@  discard block
 block discarded – undo
509 509
 
510 510
 	public function getNodeFromCacheEntryAndMount(ICacheEntry $cacheEntry, IMountPoint $mountPoint): INode {
511 511
 		$path = $cacheEntry->getPath();
512
-		$fullPath = $mountPoint->getMountPoint() . $path;
512
+		$fullPath = $mountPoint->getMountPoint().$path;
513 513
 		// todo: LazyNode?
514 514
 		$info = new FileInfo($fullPath, $mountPoint->getStorage(), $path, $cacheEntry, $mountPoint);
515 515
 		$parentPath = dirname($fullPath);
516
-		$parent = new LazyFolder($this, function () use ($parentPath) {
516
+		$parent = new LazyFolder($this, function() use ($parentPath) {
517 517
 			$parent = $this->get($parentPath);
518 518
 			if ($parent instanceof \OCP\Files\Folder) {
519 519
 				return $parent;
Please login to merge, or discard this patch.