From 76969c6a69faf3c11f1341ea183de13a817a49ca Mon Sep 17 00:00:00 2001 From: Louis Chemineau Date: Tue, 16 Jan 2024 11:36:19 +0100 Subject: [PATCH] Handle missing disk versions Signed-off-by: Louis Chemineau --- lib/Versions/GroupVersionsExpireManager.php | 11 +++- lib/Versions/VersionsBackend.php | 63 +++++++++++++++------ 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/lib/Versions/GroupVersionsExpireManager.php b/lib/Versions/GroupVersionsExpireManager.php index 377e20938..a857d980e 100644 --- a/lib/Versions/GroupVersionsExpireManager.php +++ b/lib/Versions/GroupVersionsExpireManager.php @@ -23,13 +23,14 @@ namespace OCA\GroupFolders\Versions; -use OC\Files\FileInfo; +use OCP\Files\FileInfo; use OC\Files\View; use OC\Hooks\BasicEmitter; use OC\User\User; use OCA\GroupFolders\Folder\FolderManager; use OCP\AppFramework\Utility\ITimeFactory; use OCP\EventDispatcher\IEventDispatcher; +use OCP\IUser; class GroupVersionsExpireManager extends BasicEmitter { private $folderManager; @@ -66,9 +67,17 @@ public function expireAll(): void { public function expireFolder(array $folder): void { $view = new View('/__groupfolders/versions/' . $folder['id']); $files = $this->versionsBackend->getAllVersionedFiles($folder); + /** @var IUser */ $dummyUser = new User('', null, $this->dispatcher); foreach ($files as $fileId => $file) { if ($file instanceof FileInfo) { + // Some versions could have been lost during move operations across storage. + // When this is the case, the fileinfo's path will not contains the name. + // When this is the case, we unlink the version's folder for the fileid, and continue to the next file. + if (!str_ends_with($file->getPath(), $file->getName())) { + $view->unlink('/' . $fileId); + continue; + } $versions = $this->versionsBackend->getVersionsForFile($dummyUser, $file); $expireVersions = $this->expireManager->getExpiredVersion($versions, $this->timeFactory->getTime(), false); foreach ($expireVersions as $version) { diff --git a/lib/Versions/VersionsBackend.php b/lib/Versions/VersionsBackend.php index 6235c0639..1440c691c 100644 --- a/lib/Versions/VersionsBackend.php +++ b/lib/Versions/VersionsBackend.php @@ -67,8 +67,15 @@ public function getVersionsForFile(IUser $user, FileInfo $file): array { try { $folderId = $mount->getFolderId(); - /** @var Folder $versionsFolder */ - $versionsFolder = $this->getVersionsFolder($mount->getFolderId())->get((string)$file->getId()); + + try { + $groupfoldersVersionsFolder = $this->getVersionsFolder($mount->getFolderId()); + /** @var Folder $versionsFolder */ + $versionsFolder = $groupfoldersVersionsFolder->get((string)$file->getId()); + } catch (NotFoundException $e) { + // The folder for the file's versions might not exists if no versions has been create yet. + $versionsFolder = $groupfoldersVersionsFolder->newFolder((string)$file->getId()); + } $versions = $this->getVersionsForFileFromDB($file, $user, $folderId); @@ -131,23 +138,43 @@ private function getVersionsForFileFromDB(FileInfo $fileInfo, IUser $user, int $ $folder = $this->appFolder->get((string)$folderId); $file = $folder->get($fileInfo->getInternalPath()); - return array_map( - fn (GroupVersionEntity $versionEntity) => new GroupVersion( - $versionEntity->getTimestamp(), - $versionEntity->getTimestamp(), - $file->getName(), - $versionEntity->getSize(), - $this->mimeTypeLoader->getMimetypeById($versionEntity->getMimetype()), - $mountPoint->getInternalPath($file->getPath()), - $file, - $this, - $user, - $versionEntity->getLabel(), - $file->getMtime() === $versionEntity->getTimestamp() ? $file : $versionsFolder->get((string)$versionEntity->getTimestamp()), - $folderId, - ), - $this->groupVersionsMapper->findAllVersionsForFileId($file->getId()) + $versionEntities = $this->groupVersionsMapper->findAllVersionsForFileId($fileInfo->getId()); + $mappedVersions = array_map( + function (GroupVersionEntity $versionEntity) use ($versionsFolder, $mountPoint, $file, $fileInfo, $user, $folderId) { + if ($fileInfo->getMtime() === $versionEntity->getTimestamp()) { + $versionFile = $file; + } else { + try { + $versionFile = $versionsFolder->get((string)$versionEntity->getTimestamp()); + } catch (NotFoundException $e) { + // The version does not exists on disk anymore, so we can delete its entity in the DB. + // The reality is that the disk version might have been lost during a move operation between storages, + // and its not possible to recover it, so removing the entity makes sense. + // TODO: same logic in main version backend? + $this->groupVersionsMapper->delete($versionEntity); + return null; + } + } + + return new GroupVersion( + $versionEntity->getTimestamp(), + $versionEntity->getTimestamp(), + $fileInfo->getName(), + $versionEntity->getSize(), + $this->mimeTypeLoader->getMimetypeById($versionEntity->getMimetype()), + $mountPoint->getInternalPath($fileInfo->getPath()), + $fileInfo, + $this, + $user, + $versionEntity->getLabel(), + $versionFile, + $folderId, + ); + }, + $versionEntities, ); + // Filter out null values. + return array_filter($mappedVersions); } /**