From 9c749190156292ed40c7e649d65d64b494c0f8ef Mon Sep 17 00:00:00 2001 From: Julius Knorr Date: Fri, 29 May 2026 15:53:08 +0200 Subject: [PATCH 1/3] fix(occ): resolve info:file for files in the trashbin The info:file command scoped the file id lookup to the user files folder, so files in the trashbin could not be resolved. Fall back to searching the user root which also covers files_trashbin and files_versions. AI-assistant: Claude Code 2.1.156 (Claude Opus 4.8) Co-Authored-By: Claude Opus 4.8 Signed-off-by: Julius Knorr --- core/Command/Info/FileUtils.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/Command/Info/FileUtils.php b/core/Command/Info/FileUtils.php index e106fc7a77aa2..5cb41fe52f768 100644 --- a/core/Command/Info/FileUtils.php +++ b/core/Command/Info/FileUtils.php @@ -79,7 +79,12 @@ public function getNode(string $fileInput): ?Node { } $mount = reset($mounts); $userFolder = $this->rootFolder->getUserFolder($mount->getUser()->getUID()); - return $userFolder->getFirstNodeById((int)$fileInput); + $node = $userFolder->getFirstNodeById((int)$fileInput); + if ($node) { + return $node; + } + // the file might live outside of the user files folder, e.g. in the trashbin or versions + return $userFolder->getParent()->getFirstNodeById((int)$fileInput); } else { try { return $this->rootFolder->get($fileInput); From a3b275eeaeb77c4ef5e13eed93869a0add6a379e Mon Sep 17 00:00:00 2001 From: Julius Knorr Date: Fri, 29 May 2026 15:59:27 +0200 Subject: [PATCH 2/3] fix(occ): resolve info:file for appdata and other rootless files Files that are not part of any user mount, such as appdata on the root storage, could not be resolved by id. Fall back to looking the file up directly on the root storage when no user mount matches. AI-assistant: Claude Code 2.1.156 (Claude Opus 4.8) Co-Authored-By: Claude Opus 4.8 Signed-off-by: Julius Knorr --- core/Command/Info/FileUtils.php | 44 ++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/core/Command/Info/FileUtils.php b/core/Command/Info/FileUtils.php index 5cb41fe52f768..811813483095a 100644 --- a/core/Command/Info/FileUtils.php +++ b/core/Command/Info/FileUtils.php @@ -73,18 +73,23 @@ public function getFilesByUser(FileInfo $file): array { */ public function getNode(string $fileInput): ?Node { if (is_numeric($fileInput)) { - $mounts = $this->userMountCache->getMountsForFileId((int)$fileInput); - if (!$mounts) { - return null; - } - $mount = reset($mounts); - $userFolder = $this->rootFolder->getUserFolder($mount->getUser()->getUID()); - $node = $userFolder->getFirstNodeById((int)$fileInput); - if ($node) { - return $node; + $id = (int)$fileInput; + $mounts = $this->userMountCache->getMountsForFileId($id); + if ($mounts) { + $mount = reset($mounts); + $userFolder = $this->rootFolder->getUserFolder($mount->getUser()->getUID()); + $node = $userFolder->getFirstNodeById($id); + if ($node) { + return $node; + } + // the file might live outside of the user files folder, e.g. in the trashbin or versions + $node = $userFolder->getParent()->getFirstNodeById($id); + if ($node) { + return $node; + } } - // the file might live outside of the user files folder, e.g. in the trashbin or versions - return $userFolder->getParent()->getFirstNodeById((int)$fileInput); + // the file might not belong to a user at all, e.g. appdata on the root storage + return $this->getNodeFromRootMount($id); } else { try { return $this->rootFolder->get($fileInput); @@ -94,6 +99,23 @@ public function getNode(string $fileInput): ?Node { } } + /** + * Resolve a file id directly on the root storage, covering files that are + * not part of any user mount such as appdata. + */ + private function getNodeFromRootMount(int $id): ?Node { + $mount = $this->rootFolder->getMount(''); + $storage = $mount->getStorage(); + if ($storage === null) { + return null; + } + $cacheEntry = $storage->getCache()->get($id); + if ($cacheEntry === false) { + return null; + } + return $this->rootFolder->getNodeFromCacheEntryAndMount($cacheEntry, $mount); + } + public function formatPermissions(string $type, int $permissions): string { if ($permissions == Constants::PERMISSION_ALL || ($type === 'file' && $permissions == (Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE))) { return 'full permissions'; From 23c4ab3bea1a85ba8365e8ef1a41e3a122fd204a Mon Sep 17 00:00:00 2001 From: Julius Knorr Date: Fri, 29 May 2026 16:02:49 +0200 Subject: [PATCH 3/3] fix(sharing): indicate trashed files when listing orphan shares A shared file moved to the trashbin keeps its file id, so it is flagged as an orphan share while still existing in the filecache. Detect this case and tell the admin the file is in the share owner's trashbin instead of the generic lost-access message. AI-assistant: Claude Code 2.1.156 (Claude Opus 4.8) Co-Authored-By: Claude Opus 4.8 Signed-off-by: Julius Knorr --- apps/files_sharing/lib/Command/DeleteOrphanShares.php | 6 +++++- apps/files_sharing/lib/OrphanHelper.php | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/files_sharing/lib/Command/DeleteOrphanShares.php b/apps/files_sharing/lib/Command/DeleteOrphanShares.php index e15581b4113cf..71c0c137754d8 100644 --- a/apps/files_sharing/lib/Command/DeleteOrphanShares.php +++ b/apps/files_sharing/lib/Command/DeleteOrphanShares.php @@ -52,7 +52,11 @@ public function execute(InputInterface $input, OutputInterface $output): int { $exists = $this->orphanHelper->fileExists($share['fileid']); $output->writeln("{$share['target']} owned by {$share['owner']}"); if ($exists) { - $output->writeln(" file still exists but the share owner lost access to it, run occ info:file {$share['fileid']} for more information about the file"); + if ($this->orphanHelper->isInTrashbin($share['fileid'])) { + $output->writeln(" file is in the trashbin of the share owner, run occ info:file {$share['fileid']} for more information about the file"); + } else { + $output->writeln(" file still exists but the share owner lost access to it, run occ info:file {$share['fileid']} for more information about the file"); + } } else { $output->writeln(' file no longer exists'); } diff --git a/apps/files_sharing/lib/OrphanHelper.php b/apps/files_sharing/lib/OrphanHelper.php index ed3a355263544..4b81434aa2866 100644 --- a/apps/files_sharing/lib/OrphanHelper.php +++ b/apps/files_sharing/lib/OrphanHelper.php @@ -56,6 +56,15 @@ public function fileExists(int $fileId): bool { return $query->executeQuery()->fetchOne() !== false; } + public function isInTrashbin(int $fileId): bool { + $query = $this->connection->getQueryBuilder(); + $query->select('path') + ->from('filecache') + ->where($query->expr()->eq('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); + $path = $query->executeQuery()->fetchOne(); + return $path !== false && str_starts_with($path, 'files_trashbin/'); + } + /** * @return \Traversable */