fix(libsync): folder move or rename data loss.#9998
Conversation
1fef7ae to
e14e53b
Compare
|
|
/backport to stable-33.0 |
f4873b5 to
e5c6a8e
Compare
8db218a to
534aca4
Compare
e22d5c0 to
6923867
Compare
|
8a1fd92 to
383d274
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. - Add test to cover removeRecursively sibling prefix guard. - Add isPathInsideDeletedDir as an inline header helper and a unit test verifying a sibling such as "A/BC" is not treated as a child of "A/B", guarding the journal cleanup after a failed local remove. Use qWarning instead of the deprecated QWARN in the testMovedWithError data rows. Assisted-by: Claude Code:claude-opus-4-8 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 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. Force remote rediscovery on both 412 and 423 upload errors so the lock state cleared from the journal is refreshed from the server, not only the bad etag on 412. Strengthen the 412 test to assert the parent folder etag is invalidated. Add a matching Q_OS_WIN guard in testLockedStateClearedOnClientInitiatedRename to clear the ACL before the local directory rename, which Windows requires when files inside have an ACL-based read-only restriction. Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Camila Ayres <hello@camilasan.com>
When a remote directory move is finalized, iterate all children recorded in the sync journal and rewrite their paths to reflect the new location, clearing stale lock state. Without this, children retained the old path in the database and could be treated as new uploads or silently lost on the next sync cycle. Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Camila Ayres <hello@camilasan.com>
Signed-off-by: Camila Ayres <hello@camilasan.com>
Signed-off-by: Camila Ayres <hello@camilasan.com>
79d783e to
f19c243
Compare
Signed-off-by: Camila Ayres <hello@camilasan.com>
|
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 in 2 ways:
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)