diff --git a/lib/Controller/LockingController.php b/lib/Controller/LockingController.php index a0640930..2dfd1459 100644 --- a/lib/Controller/LockingController.php +++ b/lib/Controller/LockingController.php @@ -151,9 +151,14 @@ public function unlockFolder(int $id, ?string $shareToken = null): DataResponse throw new OCSForbiddenException($this->l10n->t('You are not allowed to remove the lock')); } - $this->fileService->finalizeChanges($nodes[0]); + $touchFoldersIds = $this->metaDataStorage->getTouchedFolders($token); + foreach ($touchFoldersIds as $folderId) { + $this->fileService->finalizeChanges($userFolder->getById($folderId)[0]); - $this->metaDataStorage->saveIntermediateFile($ownerId, $id); + $this->metaDataStorage->saveIntermediateFile($ownerId, $folderId); + } + + $this->metaDataStorage->clearTouchedFolders($token); try { $this->lockManager->unlockFile($id, $token); diff --git a/lib/Controller/MetaDataController.php b/lib/Controller/MetaDataController.php index f772de7d..2c839c3b 100644 --- a/lib/Controller/MetaDataController.php +++ b/lib/Controller/MetaDataController.php @@ -136,7 +136,7 @@ public function updateMetaData(int $id, string $metaData): DataResponse { } try { - $this->metaDataStorage->updateMetaDataIntoIntermediateFile($this->userId, $id, $metaData); + $this->metaDataStorage->updateMetaDataIntoIntermediateFile($this->userId, $id, $metaData, $e2eToken); } catch (MissingMetaDataException $e) { throw new OCSNotFoundException($this->l10n->t('Metadata-file does not exist')); } catch (NotFoundException $e) { @@ -201,7 +201,7 @@ public function addMetadataFileDrop(int $id, string $fileDrop, ?string $shareTok $decodedMetadata['filedrop'] = array_merge($decodedMetadata['filedrop'] ?? [], $decodedFileDrop); $encodedMetadata = json_encode($decodedMetadata); - $this->metaDataStorage->updateMetaDataIntoIntermediateFile($ownerId, $id, $encodedMetadata); + $this->metaDataStorage->updateMetaDataIntoIntermediateFile($ownerId, $id, $encodedMetadata, $e2eToken); } catch (MissingMetaDataException $e) { throw new OCSNotFoundException($this->l10n->t('Metadata-file does not exist')); } catch (NotFoundException $e) { diff --git a/lib/FileService.php b/lib/FileService.php index ea21df26..266a9980 100644 --- a/lib/FileService.php +++ b/lib/FileService.php @@ -56,7 +56,7 @@ public function revertChanges(Folder $folder): bool { } /** - * @return bool Whether this operation changed any files + * @return bool Move and delete temporary files suffixed by .e2e-to-save and .e2e-to-delete */ public function finalizeChanges(Folder $folder): bool { $intermediateFiles = $this->getIntermediateFiles($folder); diff --git a/lib/IMetaDataStorage.php b/lib/IMetaDataStorage.php index 348c8c81..a20e9043 100644 --- a/lib/IMetaDataStorage.php +++ b/lib/IMetaDataStorage.php @@ -58,7 +58,7 @@ public function setMetaDataIntoIntermediateFile(string $userId, int $id, string * @throws NotFoundException * @throws MissingMetaDataException */ - public function updateMetaDataIntoIntermediateFile(string $userId, int $id, string $fileKey): void; + public function updateMetaDataIntoIntermediateFile(string $userId, int $id, string $fileKey, string $token = null): void; /** * Moves intermediate metadata file to final file @@ -84,4 +84,22 @@ public function deleteIntermediateFile(string $userId, int $id): void; * @throws NotFoundException */ public function deleteMetaData(string $userId, int $id): void; + + /** + * Return the list of folders marked as touched. + * + * @return int[] + * + * @throws NotPermittedException + * @throws NotFoundException + */ + public function getTouchedFolders(string $token): array; + + /** + * Clear the list of touched folder for a token. + * + * @throws NotPermittedException + * @throws NotFoundException + */ + public function clearTouchedFolders(?string $token): void; } diff --git a/lib/MetaDataStorage.php b/lib/MetaDataStorage.php index 4958f339..c09a2f7f 100644 --- a/lib/MetaDataStorage.php +++ b/lib/MetaDataStorage.php @@ -106,7 +106,7 @@ public function setMetaDataIntoIntermediateFile(string $userId, int $id, string /** * @inheritDoc */ - public function updateMetaDataIntoIntermediateFile(string $userId, int $id, string $fileKey): void { + public function updateMetaDataIntoIntermediateFile(string $userId, int $id, string $fileKey, string $token = null): void { // ToDo check signature for race condition $this->verifyFolderStructure(); $this->verifyOwner($userId, $id); @@ -136,6 +136,18 @@ public function updateMetaDataIntoIntermediateFile(string $userId, int $id, stri $intermediateMetaDataFile ->putContent($fileKey); + + // To ease the wrap-up process during unlocking, + // we keep track of every folder for which metadata was updated. + // For that we create a file named /tokens/$token/$folderId. + if ($token !== null) { + try { + $tokenFolder = $this->appData->getFolder("/tokens/$token"); + } catch (NotFoundException $ex) { + $tokenFolder = $this->appData->newFolder("/tokens/$token"); + } + $tokenFolder->newFile("$id", ''); + } } /** @@ -308,4 +320,23 @@ protected function getLegacyOwnerPath(string $userId, int $id):string { return $ownerNodes[0]->getPath(); } + + /** + * @inheritDoc + */ + public function getTouchedFolders(string $token): array { + return array_map( + fn (ISimpleFile $file) => (int)$file->getName(), + $this->appData->getFolder("/tokens")->getFolder($token)->getDirectoryListing() + ); + } + + /** + * @inheritDoc + */ + public function clearTouchedFolders(?string $token): void { + if ($token !== null) { + $this->appData->getFolder("/tokens/$token")->delete(); + } + } } diff --git a/lib/RollbackService.php b/lib/RollbackService.php index 4a154e34..9cc1f1d1 100644 --- a/lib/RollbackService.php +++ b/lib/RollbackService.php @@ -117,6 +117,8 @@ public function rollbackOlderThan(int $olderThanTimestamp, ?int $limit = null): continue; } + $this->metaDataStorage->clearTouchedFolders($lock->getToken()); + $this->lockMapper->delete($lock); } }