aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Files
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/Files')
-rw-r--r--lib/private/Files/Node/Root.php2
-rw-r--r--lib/private/Files/ObjectStore/InvalidObjectStoreConfigurationException.php13
-rw-r--r--lib/private/Files/ObjectStore/PrimaryObjectStoreConfig.php121
-rw-r--r--lib/private/Files/View.php44
4 files changed, 130 insertions, 50 deletions
diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php
index d82ec08f362..76afca9dee8 100644
--- a/lib/private/Files/Node/Root.php
+++ b/lib/private/Files/Node/Root.php
@@ -415,7 +415,7 @@ class Root extends Folder implements IRootFolder {
*/
public function getByIdInPath(int $id, string $path): array {
$mountCache = $this->getUserMountCache();
- if (strpos($path, '/', 1) > 0) {
+ if ($path !== '' && strpos($path, '/', 1) > 0) {
[, $user] = explode('/', $path);
} else {
$user = null;
diff --git a/lib/private/Files/ObjectStore/InvalidObjectStoreConfigurationException.php b/lib/private/Files/ObjectStore/InvalidObjectStoreConfigurationException.php
new file mode 100644
index 00000000000..369182b069d
--- /dev/null
+++ b/lib/private/Files/ObjectStore/InvalidObjectStoreConfigurationException.php
@@ -0,0 +1,13 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2025 Robin Appelman <robin@icewind.nl>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OC\Files\ObjectStore;
+
+class InvalidObjectStoreConfigurationException extends \Exception {
+
+}
diff --git a/lib/private/Files/ObjectStore/PrimaryObjectStoreConfig.php b/lib/private/Files/ObjectStore/PrimaryObjectStoreConfig.php
index fdfe989addc..ffc33687340 100644
--- a/lib/private/Files/ObjectStore/PrimaryObjectStoreConfig.php
+++ b/lib/private/Files/ObjectStore/PrimaryObjectStoreConfig.php
@@ -34,9 +34,13 @@ class PrimaryObjectStoreConfig {
* @return ?ObjectStoreConfig
*/
public function getObjectStoreConfigForRoot(): ?array {
- $config = $this->getObjectStoreConfig();
+ if (!$this->hasObjectStore()) {
+ return null;
+ }
+
+ $config = $this->getObjectStoreConfiguration('root');
- if ($config && $config['arguments']['multibucket']) {
+ if ($config['arguments']['multibucket']) {
if (!isset($config['arguments']['bucket'])) {
$config['arguments']['bucket'] = '';
}
@@ -51,38 +55,102 @@ class PrimaryObjectStoreConfig {
* @return ?ObjectStoreConfig
*/
public function getObjectStoreConfigForUser(IUser $user): ?array {
- $config = $this->getObjectStoreConfig();
+ if (!$this->hasObjectStore()) {
+ return null;
+ }
- if ($config && $config['arguments']['multibucket']) {
+ $store = $this->getObjectStoreForUser($user);
+ $config = $this->getObjectStoreConfiguration($store);
+
+ if ($config['arguments']['multibucket']) {
$config['arguments']['bucket'] = $this->getBucketForUser($user, $config);
}
return $config;
}
/**
- * @return ?ObjectStoreConfig
+ * @param string $name
+ * @return ObjectStoreConfig
*/
- private function getObjectStoreConfig(): ?array {
+ public function getObjectStoreConfiguration(string $name): array {
+ $configs = $this->getObjectStoreConfigs();
+ $name = $this->resolveAlias($name);
+ if (!isset($configs[$name])) {
+ throw new \Exception("Object store configuration for '$name' not found");
+ }
+ if (is_string($configs[$name])) {
+ throw new \Exception("Object store configuration for '{$configs[$name]}' not found");
+ }
+ return $configs[$name];
+ }
+
+ public function resolveAlias(string $name): string {
+ $configs = $this->getObjectStoreConfigs();
+
+ while (isset($configs[$name]) && is_string($configs[$name])) {
+ $name = $configs[$name];
+ }
+ return $name;
+ }
+
+ public function hasObjectStore(): bool {
+ $objectStore = $this->config->getSystemValue('objectstore', null);
+ $objectStoreMultiBucket = $this->config->getSystemValue('objectstore_multibucket', null);
+ return $objectStore || $objectStoreMultiBucket;
+ }
+
+ public function hasMultipleObjectStorages(): bool {
+ $objectStore = $this->config->getSystemValue('objectstore', []);
+ return isset($objectStore['default']);
+ }
+
+ /**
+ * @return ?array<string, ObjectStoreConfig|string>
+ * @throws InvalidObjectStoreConfigurationException
+ */
+ public function getObjectStoreConfigs(): ?array {
$objectStore = $this->config->getSystemValue('objectstore', null);
$objectStoreMultiBucket = $this->config->getSystemValue('objectstore_multibucket', null);
// new-style multibucket config uses the same 'objectstore' key but sets `'multibucket' => true`, transparently upgrade older style config
if ($objectStoreMultiBucket) {
$objectStoreMultiBucket['arguments']['multibucket'] = true;
- return $this->validateObjectStoreConfig($objectStoreMultiBucket);
+ return [
+ 'default' => 'server1',
+ 'server1' => $this->validateObjectStoreConfig($objectStoreMultiBucket),
+ 'root' => 'server1',
+ ];
} elseif ($objectStore) {
- return $this->validateObjectStoreConfig($objectStore);
+ if (!isset($objectStore['default'])) {
+ $objectStore = [
+ 'default' => 'server1',
+ 'root' => 'server1',
+ 'server1' => $objectStore,
+ ];
+ }
+ if (!isset($objectStore['root'])) {
+ $objectStore['root'] = 'default';
+ }
+
+ if (!is_string($objectStore['default'])) {
+ throw new InvalidObjectStoreConfigurationException('The \'default\' object storage configuration is required to be a reference to another configuration.');
+ }
+ return array_map($this->validateObjectStoreConfig(...), $objectStore);
} else {
return null;
}
}
/**
- * @return ObjectStoreConfig
+ * @param array|string $config
+ * @return string|ObjectStoreConfig
*/
- private function validateObjectStoreConfig(array $config) {
+ private function validateObjectStoreConfig(array|string $config): array|string {
+ if (is_string($config)) {
+ return $config;
+ }
if (!isset($config['class'])) {
- throw new \Exception('No class configured for object store');
+ throw new InvalidObjectStoreConfigurationException('No class configured for object store');
}
if (!isset($config['arguments'])) {
$config['arguments'] = [];
@@ -90,17 +158,17 @@ class PrimaryObjectStoreConfig {
$class = $config['class'];
$arguments = $config['arguments'];
if (!is_array($arguments)) {
- throw new \Exception('Configured object store arguments are not an array');
+ throw new InvalidObjectStoreConfigurationException('Configured object store arguments are not an array');
}
if (!isset($arguments['multibucket'])) {
$arguments['multibucket'] = false;
}
if (!is_bool($arguments['multibucket'])) {
- throw new \Exception('arguments.multibucket must be a boolean in object store configuration');
+ throw new InvalidObjectStoreConfigurationException('arguments.multibucket must be a boolean in object store configuration');
}
if (!is_string($class)) {
- throw new \Exception('Configured class for object store is not a string');
+ throw new InvalidObjectStoreConfigurationException('Configured class for object store is not a string');
}
if (str_starts_with($class, 'OCA\\') && substr_count($class, '\\') >= 2) {
@@ -109,7 +177,7 @@ class PrimaryObjectStoreConfig {
}
if (!is_a($class, IObjectStore::class, true)) {
- throw new \Exception('Configured class for object store is not an object store');
+ throw new InvalidObjectStoreConfigurationException('Configured class for object store is not an object store');
}
return [
'class' => $class,
@@ -117,8 +185,8 @@ class PrimaryObjectStoreConfig {
];
}
- private function getBucketForUser(IUser $user, array $config): string {
- $bucket = $this->config->getUserValue($user->getUID(), 'homeobjectstore', 'bucket', null);
+ public function getBucketForUser(IUser $user, array $config): string {
+ $bucket = $this->getSetBucketForUser($user);
if ($bucket === null) {
/*
@@ -129,7 +197,7 @@ class PrimaryObjectStoreConfig {
$config['arguments']['bucket'] = '';
}
$mapper = new Mapper($user, $this->config);
- $numBuckets = isset($config['arguments']['num_buckets']) ? $config['arguments']['num_buckets'] : 64;
+ $numBuckets = $config['arguments']['num_buckets'] ?? 64;
$bucket = $config['arguments']['bucket'] . $mapper->getBucket($numBuckets);
$this->config->setUserValue($user->getUID(), 'homeobjectstore', 'bucket', $bucket);
@@ -137,4 +205,21 @@ class PrimaryObjectStoreConfig {
return $bucket;
}
+
+ public function getSetBucketForUser(IUser $user): ?string {
+ return $this->config->getUserValue($user->getUID(), 'homeobjectstore', 'bucket', null);
+ }
+
+ public function getObjectStoreForUser(IUser $user): string {
+ if ($this->hasMultipleObjectStorages()) {
+ $value = $this->config->getUserValue($user->getUID(), 'homeobjectstore', 'objectstore', null);
+ if ($value === null) {
+ $value = $this->resolveAlias('default');
+ $this->config->setUserValue($user->getUID(), 'homeobjectstore', 'objectstore', $value);
+ }
+ return $value;
+ } else {
+ return 'default';
+ }
+ }
}
diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php
index 63eecf5e1d6..a852f453963 100644
--- a/lib/private/Files/View.php
+++ b/lib/private/Files/View.php
@@ -1828,43 +1828,25 @@ class View {
* @return string
* @throws NotFoundException
*/
- public function getPath($id, ?int $storageId = null) {
+ public function getPath($id, ?int $storageId = null): string {
$id = (int)$id;
- $manager = Filesystem::getMountManager();
- $mounts = $manager->findIn($this->fakeRoot);
- $mounts[] = $manager->find($this->fakeRoot);
- $mounts = array_filter($mounts);
- // reverse the array, so we start with the storage this view is in
- // which is the most likely to contain the file we're looking for
- $mounts = array_reverse($mounts);
-
- // put non-shared mounts in front of the shared mount
- // this prevents unneeded recursion into shares
- usort($mounts, function (IMountPoint $a, IMountPoint $b) {
- return $a instanceof SharedMount && (!$b instanceof SharedMount) ? 1 : -1;
- });
+ $rootFolder = Server::get(Files\IRootFolder::class);
- if (!is_null($storageId)) {
- $mounts = array_filter($mounts, function (IMountPoint $mount) use ($storageId) {
- return $mount->getNumericStorageId() === $storageId;
- });
+ $node = $rootFolder->getFirstNodeByIdInPath($id, $this->getRoot());
+ if ($node) {
+ if ($storageId === null || $storageId === $node->getStorage()->getCache()->getNumericStorageId()) {
+ return $this->getRelativePath($node->getPath()) ?? '';
+ }
+ } else {
+ throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id));
}
- foreach ($mounts as $mount) {
- /**
- * @var \OC\Files\Mount\MountPoint $mount
- */
- if ($mount->getStorage()) {
- $cache = $mount->getStorage()->getCache();
- $internalPath = $cache->getPathById($id);
- if (is_string($internalPath)) {
- $fullPath = $mount->getMountPoint() . $internalPath;
- if (!is_null($path = $this->getRelativePath($fullPath))) {
- return $path;
- }
- }
+ foreach ($rootFolder->getByIdInPath($id, $this->getRoot()) as $node) {
+ if ($storageId === $node->getStorage()->getCache()->getNumericStorageId()) {
+ return $this->getRelativePath($node->getPath()) ?? '';
}
}
+
throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id));
}