mirror of
https://github.com/nextcloud/server.git
synced 2026-04-15 22:11:17 -04:00
optimize getById on LazyUserFolder to not require a full fs setup
Signed-off-by: Robin Appelman <robin@icewind.nl>
This commit is contained in:
parent
700444e218
commit
44a8ebdc1f
6 changed files with 121 additions and 82 deletions
|
|
@ -333,70 +333,7 @@ class Folder extends Node implements \OCP\Files\Folder {
|
|||
* @return \OC\Files\Node\Node[]
|
||||
*/
|
||||
public function getById($id) {
|
||||
$mountCache = $this->root->getUserMountCache();
|
||||
if (strpos($this->getPath(), '/', 1) > 0) {
|
||||
[, $user] = explode('/', $this->getPath());
|
||||
} else {
|
||||
$user = null;
|
||||
}
|
||||
$mountsContainingFile = $mountCache->getMountsForFileId((int)$id, $user);
|
||||
|
||||
// when a user has access trough the same storage trough multiple paths
|
||||
// (such as an external storage that is both mounted for a user and shared to the user)
|
||||
// the mount cache will only hold a single entry for the storage
|
||||
// this can lead to issues as the different ways the user has access to a storage can have different permissions
|
||||
//
|
||||
// so instead of using the cached entries directly, we instead filter the current mounts by the rootid of the cache entry
|
||||
|
||||
$mountRootIds = array_map(function ($mount) {
|
||||
return $mount->getRootId();
|
||||
}, $mountsContainingFile);
|
||||
$mountRootPaths = array_map(function ($mount) {
|
||||
return $mount->getRootInternalPath();
|
||||
}, $mountsContainingFile);
|
||||
$mountRoots = array_combine($mountRootIds, $mountRootPaths);
|
||||
|
||||
$mounts = $this->root->getMountsIn($this->path);
|
||||
$mounts[] = $this->root->getMount($this->path);
|
||||
|
||||
$mountsContainingFile = array_filter($mounts, function ($mount) use ($mountRoots) {
|
||||
return isset($mountRoots[$mount->getStorageRootId()]);
|
||||
});
|
||||
|
||||
if (count($mountsContainingFile) === 0) {
|
||||
if ($user === $this->getAppDataDirectoryName()) {
|
||||
return $this->getByIdInRootMount((int)$id);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
$nodes = array_map(function (IMountPoint $mount) use ($id, $mountRoots) {
|
||||
$rootInternalPath = $mountRoots[$mount->getStorageRootId()];
|
||||
$cacheEntry = $mount->getStorage()->getCache()->get((int)$id);
|
||||
if (!$cacheEntry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// cache jails will hide the "true" internal path
|
||||
$internalPath = ltrim($rootInternalPath . '/' . $cacheEntry->getPath(), '/');
|
||||
$pathRelativeToMount = substr($internalPath, strlen($rootInternalPath));
|
||||
$pathRelativeToMount = ltrim($pathRelativeToMount, '/');
|
||||
$absolutePath = rtrim($mount->getMountPoint() . $pathRelativeToMount, '/');
|
||||
return $this->root->createNode($absolutePath, new \OC\Files\FileInfo(
|
||||
$absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
|
||||
\OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
|
||||
));
|
||||
}, $mountsContainingFile);
|
||||
|
||||
$nodes = array_filter($nodes);
|
||||
|
||||
$folders = array_filter($nodes, function (Node $node) {
|
||||
return $this->getRelativePath($node->getPath());
|
||||
});
|
||||
usort($folders, function ($a, $b) {
|
||||
return $b->getPath() <=> $a->getPath();
|
||||
});
|
||||
return $folders;
|
||||
return $this->root->getByIdInPath((int)$id, $this->getPath());
|
||||
}
|
||||
|
||||
protected function getAppDataDirectoryName(): string {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ declare(strict_types=1);
|
|||
*/
|
||||
namespace OC\Files\Node;
|
||||
|
||||
use OC\Files\Utils\PathHelper;
|
||||
use OCP\Constants;
|
||||
|
||||
/**
|
||||
|
|
@ -379,13 +380,6 @@ class LazyFolder implements \OCP\Files\Folder {
|
|||
return $this->__call(__FUNCTION__, func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getRelativePath($path) {
|
||||
return $this->__call(__FUNCTION__, func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
|
@ -518,4 +512,8 @@ class LazyFolder implements \OCP\Files\Folder {
|
|||
public function getUploadTime(): int {
|
||||
return $this->__call(__FUNCTION__, func_get_args());
|
||||
}
|
||||
|
||||
public function getRelativePath($path) {
|
||||
return PathHelper::getRelativePath($this->getPath(), $path);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,4 +39,8 @@ class LazyRoot extends LazyFolder implements IRootFolder {
|
|||
public function getUserFolder($userId) {
|
||||
return $this->__call(__FUNCTION__, func_get_args());
|
||||
}
|
||||
|
||||
public function getByIdInPath(int $id, string $path) {
|
||||
return $this->__call(__FUNCTION__, func_get_args());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,28 +29,38 @@ use OCP\Files\NotFoundException;
|
|||
use OCP\IUser;
|
||||
|
||||
class LazyUserFolder extends LazyFolder {
|
||||
private IRootFolder $rootFolder;
|
||||
private IRootFolder $root;
|
||||
private IUser $user;
|
||||
private string $path;
|
||||
|
||||
public function __construct(IRootFolder $rootFolder, IUser $user) {
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->root = $rootFolder;
|
||||
$this->user = $user;
|
||||
$this->path = '/' . $user->getUID() . '/files';
|
||||
parent::__construct(function () use ($user) {
|
||||
try {
|
||||
return $this->rootFolder->get('/' . $user->getUID() . '/files');
|
||||
return $this->root->get('/' . $user->getUID() . '/files');
|
||||
} catch (NotFoundException $e) {
|
||||
if (!$this->rootFolder->nodeExists('/' . $user->getUID())) {
|
||||
$this->rootFolder->newFolder('/' . $user->getUID());
|
||||
if (!$this->root->nodeExists('/' . $user->getUID())) {
|
||||
$this->root->newFolder('/' . $user->getUID());
|
||||
}
|
||||
return $this->rootFolder->newFolder('/' . $user->getUID() . '/files');
|
||||
return $this->root->newFolder('/' . $user->getUID() . '/files');
|
||||
}
|
||||
}, [
|
||||
'path' => '/' . $user->getUID() . '/files',
|
||||
'path' => $this->path,
|
||||
'permissions' => Constants::PERMISSION_ALL,
|
||||
]);
|
||||
}
|
||||
|
||||
public function get($path) {
|
||||
return $this->rootFolder->get('/' . $this->user->getUID() . '/files/' . rtrim($path, '/'));
|
||||
return $this->root->get('/' . $this->user->getUID() . '/files/' . ltrim($path, '/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return \OC\Files\Node\Node[]
|
||||
*/
|
||||
public function getById($id) {
|
||||
return $this->root->getByIdInPath((int)$id, $this->getPath());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ use OCP\EventDispatcher\IEventDispatcher;
|
|||
use OCP\Files\Config\IUserMountCache;
|
||||
use OCP\Files\Events\Node\FilesystemTornDownEvent;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\Mount\IMountPoint;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\NotPermittedException;
|
||||
use OCP\IUser;
|
||||
|
|
@ -216,7 +217,7 @@ class Root extends Folder implements IRootFolder {
|
|||
|
||||
/**
|
||||
* @param string $targetPath
|
||||
* @return \OC\Files\Node\Node
|
||||
* @return Node
|
||||
* @throws \OCP\Files\NotPermittedException
|
||||
*/
|
||||
public function rename($targetPath) {
|
||||
|
|
@ -229,7 +230,7 @@ class Root extends Folder implements IRootFolder {
|
|||
|
||||
/**
|
||||
* @param string $targetPath
|
||||
* @return \OC\Files\Node\Node
|
||||
* @return Node
|
||||
* @throws \OCP\Files\NotPermittedException
|
||||
*/
|
||||
public function copy($targetPath) {
|
||||
|
|
@ -404,4 +405,82 @@ class Root extends Folder implements IRootFolder {
|
|||
public function getUserMountCache() {
|
||||
return $this->userMountCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return Node[]
|
||||
*/
|
||||
public function getByIdInPath(int $id, string $path): array {
|
||||
$mountCache = $this->getUserMountCache();
|
||||
if (strpos($path, '/', 1) > 0) {
|
||||
[, $user] = explode('/', $path);
|
||||
} else {
|
||||
$user = null;
|
||||
}
|
||||
$mountsContainingFile = $mountCache->getMountsForFileId($id, $user);
|
||||
|
||||
// when a user has access trough the same storage trough multiple paths
|
||||
// (such as an external storage that is both mounted for a user and shared to the user)
|
||||
// the mount cache will only hold a single entry for the storage
|
||||
// this can lead to issues as the different ways the user has access to a storage can have different permissions
|
||||
//
|
||||
// so instead of using the cached entries directly, we instead filter the current mounts by the rootid of the cache entry
|
||||
|
||||
$mountRootIds = array_map(function ($mount) {
|
||||
return $mount->getRootId();
|
||||
}, $mountsContainingFile);
|
||||
$mountRootPaths = array_map(function ($mount) {
|
||||
return $mount->getRootInternalPath();
|
||||
}, $mountsContainingFile);
|
||||
$mountProviders = array_unique(array_map(function ($mount) {
|
||||
return $mount->getMountProvider();
|
||||
}, $mountsContainingFile));
|
||||
$mountRoots = array_combine($mountRootIds, $mountRootPaths);
|
||||
|
||||
$mounts = $this->mountManager->getMountsByMountProvider($path, $mountProviders);
|
||||
|
||||
$mountsContainingFile = array_filter($mounts, function ($mount) use ($mountRoots) {
|
||||
return isset($mountRoots[$mount->getStorageRootId()]);
|
||||
});
|
||||
|
||||
if (count($mountsContainingFile) === 0) {
|
||||
if ($user === $this->getAppDataDirectoryName()) {
|
||||
$folder = $this->get($path);
|
||||
if ($folder instanceof Folder) {
|
||||
return $folder->getByIdInRootMount($id);
|
||||
} else {
|
||||
throw new \Exception("getByIdInPath with non folder");
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
$nodes = array_map(function (IMountPoint $mount) use ($id, $mountRoots) {
|
||||
$rootInternalPath = $mountRoots[$mount->getStorageRootId()];
|
||||
$cacheEntry = $mount->getStorage()->getCache()->get($id);
|
||||
if (!$cacheEntry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// cache jails will hide the "true" internal path
|
||||
$internalPath = ltrim($rootInternalPath . '/' . $cacheEntry->getPath(), '/');
|
||||
$pathRelativeToMount = substr($internalPath, strlen($rootInternalPath));
|
||||
$pathRelativeToMount = ltrim($pathRelativeToMount, '/');
|
||||
$absolutePath = rtrim($mount->getMountPoint() . $pathRelativeToMount, '/');
|
||||
return $this->createNode($absolutePath, new FileInfo(
|
||||
$absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
|
||||
\OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
|
||||
));
|
||||
}, $mountsContainingFile);
|
||||
|
||||
$nodes = array_filter($nodes);
|
||||
|
||||
$folders = array_filter($nodes, function (Node $node) use ($path) {
|
||||
return PathHelper::getRelativePath($path, $node->getPath()) !== null;
|
||||
});
|
||||
usort($folders, function ($a, $b) {
|
||||
return $b->getPath() <=> $a->getPath();
|
||||
});
|
||||
return $folders;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,11 +38,22 @@ interface IRootFolder extends Folder, Emitter {
|
|||
* Returns a view to user's files folder
|
||||
*
|
||||
* @param string $userId user ID
|
||||
* @return \OCP\Files\Folder
|
||||
* @return Folder
|
||||
* @throws NoUserException
|
||||
* @throws NotPermittedException
|
||||
*
|
||||
* @since 8.2.0
|
||||
*/
|
||||
public function getUserFolder($userId);
|
||||
|
||||
/**
|
||||
* Get a file or folder by fileid, inside a parent path
|
||||
*
|
||||
* @param int $id
|
||||
* @param string $path
|
||||
* @return Node[]
|
||||
*
|
||||
* @since 24.0.0
|
||||
*/
|
||||
public function getByIdInPath(int $id, string $path);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue