fix(libsync): folder move or rename data loss.#9998
Conversation
1fef7ae to
e14e53b
Compare
|
|
/backport to stable-33.0 |
f4873b5 to
e5c6a8e
Compare
ecd2eee to
18e5916
Compare
startsWith(deletedDir) without a trailing slash would match sibling directories sharing a common prefix (e.g. "A/B" matching "A/BC"), causing their journal records to be skipped in the failure-cleanup loop. Append '/' before the comparison. Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Camila Ayres <hello@camilasan.com>
WebDAV lock tokens are bound to the URL they were issued for. When a parent directory is renamed, PropagateLocalRename and PropagateRemoteMove both copy child journal records to the new path while preserving the old token. Subsequent uploads send the stale token and receive 412/423 from the server. Clear _lockToken before writing the new-path record in both code paths. Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Camila Ayres <hello@camilasan.com>
When a file path changes (due to a parent folder rename), any WebDAV lock token stored in the journal becomes invalid because tokens are bound to the original URL. A subsequent upload sends an If: header with the stale token, and the server replies 412 (Precondition Failed) or 423 (Locked). Clear the token from the journal on either status code so the next sync retries without it. Schedule rediscovery on 412 since the server state is uncertain. Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Camila Ayres <hello@camilasan.com>
Cover the three bug classes fixed in the standard sync engine: - testLockTokenClearedOnServerInitiatedRename: server renames a folder; verifies PropagateLocalRename clears the child's stale lock token and locked state in the journal. - testLockedStateClearedOnClientInitiatedRename: client renames a folder while the child file is server-locked; verifies PropagateRemoteMove clears the locked state in the new-path journal record. - testUpload412SchedulesRediscovery: upload gets a 412 response; verifies the file is rediscovered and successfully synced on the next attempt. - testPostRenameBulkDeletePreservesFailedChildRename: folder and child file are both renamed, child MOVE fails; verifies the old-path journal record is preserved so the next sync can recover the file. Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Camila Ayres <hello@camilasan.com>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Camila Ayres <hello@camilasan.com>
18e5916 to
8adb02e
Compare
|
Artifact containing the AppImage: nextcloud-appimage-pr-9998.zip Digest: To test this change/fix you can download the above artifact file, unzip it, and run it. Please make sure to quit your existing Nextcloud app and backup your data. |
|




Resolves
When a parent folder is renamed during sync, file changes inside it can be silently lost. Three bugs compound to cause this:
Stale lock tokens: renaming a parent copies child journal records to the new path but keeps the old WebDAV lock token. The next upload sends it at the new URL and the server rejects it with 412/423.
Clear _lockToken and _locked when writing a renamed child's journal record.
And No 412/423 recovery: the error handler didn't clear the stale token, so every retry failed the same way.
Clear the token from the journal on 412/423 and schedule rediscovery.
fix(file-provider): invalidate lock tokens when file paths change. #10135
Path prefix bug: removeRecursively used startsWith(dir) instead of startsWith(dir + '/'), causing false prefix matches on sibling paths like A/BC when processing A/B.
Use startsWith(dir + '/').
See fix(file-provider): use slash prefix for child item queries. #10130
Steps to test it
Scenario 1 — Stale lock token after folder rename (the main symptom)
See fix(file-provider): invalidate lock tokens when file paths change. #10135
✅ Expected (with fix): save succeeds, Work2/report.odt contents do not get lost.
🔴 Regression (without fix): the upload fails with 412 or 423, LibreOffice shows a save error, and every subsequent save attempt fails identically because the stale token is never cleared.
Scenario 2 — Path prefix matching (sibling folders not clobbered)
✅ Expected (with fix): D2/ and its contents remain fully intact after the sync.
🔴 Regression (without fix): because "D2".startsWith("D") is true, the old logic could mark D2's entries as part of the renamed tree and remove them from the journal.
Checklist
AI (if applicable)