Skip to content

[stable35] fix(encryption): keep version and size in sync for files not in the cache - #64485

Open
backportbot[bot] wants to merge 3 commits into
stable35from
backport/64365/stable35
Open

backportbot[bot] wants to merge 3 commits into
stable35from
backport/64365/stable35

Conversation

@backportbot

@backportbot backportbot Bot commented Sep 17, 2026

Copy link
Copy Markdown

Warning, This backport's changes differ from the original and might be incomplete ⚠️

Todo

  • Review and resolve any conflicts
  • Review and verify the backported changes
  • Amend HEAD commit to remove the line stating to skip CI

Learn more about backports at https://docs.nextcloud.com/server/stable/go.php?to=developer-backports.

@backportbot backportbot Bot added this to the Nextcloud 35.0.1 milestone Sep 17, 2026
@susnux
susnux force-pushed the backport/64365/stable35 branch from dd9a95a to 4e72faa Compare September 20, 2026 12:45
@susnux
susnux marked this pull request as ready for review September 20, 2026 12:46
@susnux
susnux requested a review from a team as a code owner September 20, 2026 12:46
@susnux
susnux requested review from Altahrim, icewind1991, provokateurin and salmart-dev and removed request for a team September 20, 2026 12:46
@Altahrim Altahrim mentioned this pull request Sep 20, 2026
…ache

A file written through a stream has no file cache entry until the caller
scans it, but both inputs of the block signature are read from that entry:
stream_close() can only bump `encryptedVersion` if the entry exists, while
the reader got version 0 instead of the 1 the blocks were signed with, and
filesize() returned the wrapped storage's ciphertext size, which moved the
'end' position marker to the wrong block. Reading such a file back - e.g.
moving a part file to a target on another storage - failed with
"Bad Signature".

Treat a missing version as 1 on read, and let the size tracked while
writing win over the wrapped storage's size even without a cache entry.
Also stop reading `encryptedVersion` off a missing source entry when
updating the encrypted version of a copy or rename.

Assisted-by: ClaudeCode:claude-opus-5
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
A copy re-encrypts the target, so the signature of its blocks is keyed on
the version of the target - the version of the file it overwrites plus one -
and not on the version of the source. Two writers overwrite that value after
the stream recorded it: updateEncryptedVersion() resets it to 1 for every
copy, and Cache::copyFromCache() then puts the source's version on the
target. Reading the copy back fails with "Bad Signature" whenever those
differ, which is the case for every copy of a file that was written more
than once and for every copy onto an existing file.

Take the version the stream recorded for the target instead of resetting it,
and let it win over the source's version when the cache entry of the copy is
written.

Assisted-by: ClaudeCode:claude-opus-5
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
The size of an encrypted file lives in `unencrypted_size`, and both
CacheEntry::getUnencryptedSize() and FileInfo::getSize() prefer it over
`size`. Cache::copyFromCache() copied the `encrypted` mark without it, so
the copy of an encrypted file was marked encrypted with an unencrypted size
of 0 and reported as empty - in the web UI, to clients and for quota - until
something rescanned it.

Copy the unencrypted size alongside the encrypted version, and reset it when
the mark is dropped for a target that is not encrypted, where the size is
read from `size`.

Assisted-by: ClaudeCode:claude-opus-5
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants