nextcloud / nextcloud/desktop

Client uploads 0-byte files overwriting server content after power loss / hard reset during sync (ext4 zero-length recovery treated as local edit)

Open
#10,547 3 comments 1 reaction 1 assignee Claimed by @mgallien View on GitHub
feature: :arrows_counterclockwise: sync engine
Dominant language
C++
Stars
3.9k
Forks
1k
Avg merge
1d 21h
Merged PRs (30d)
127

Description

## Bug description

After a hard reset (GPU driver freeze → power button) **while the desktop client was downloading files during a sync run**, the client re-uploaded 447 files with **empty content (0 bytes)**, overwriting the server-side originals that had been created by other users the same morning.

Root cause chain, fully evidenced by client + server logs:

1. Client starts a sync and downloads new remote files. The downloads complete and the sync journal DB records them (path, size, mtime, checksum). The sync DB is fsync'd; the downloaded file *data* is still in the page cache.
2. Machine hard-resets (in our case an amdgpu freeze; any power loss reproduces this). ext4 (ordered mode) journal replay preserves the file *metadata* (name, mtime) but the unflushed *data* is lost → files exist locally with **0 bytes and the original mtime**.
3. On the next start, discovery compares DB vs. local: **same mtime, different size** — and classifies this as a *local modification*:

```
[ info nextcloud.sync.discovery discovery.cpp:1892 ] Processing "…/invoice.pdf" | (db/local/remote) | valid: true/true/db | mtime: 1786005528/1786005528/0 | size: 197806/0/0 | etag: "f459…"//"" | checksum: "SHA1:ed47…"//""
[ info nextcloud.sync.discovery discovery.cpp:1893 ] discovered "…/invoice.pdf" CSyncEnums::CSYNC_INSTRUCTION_SYNC OCC::SyncFileItem::Up
[ info nextcloud.sync.propagator.upload.v1 propagateuploadv1.cpp:132 ] "/…/invoice.pdf" "SHA1:da39a3ee5e6b4b0d3255bfef95601890afd80709" ← SHA1 of the empty string
[ info nextcloud.sync.networkjob.put propagateupload.cpp:89 ] PUT of "https://…/invoice.pdf" FINISHED WITH STATUS "OK" 204
```

4. The server-side originals (created by *other* users into a group folder) were overwritten with 0 bytes. Because the upload preserves the local mtime (`X-OC-Mtime`), the damage is hard to spot: the files keep their plausible timestamps and only the size reveals the loss.

Notably, the client's own log files from the crashed boot were also zeroed by the same ext4 behaviour — confirming the mechanism.

This is the "upload" sibling of #9280 (which covers the *deletion* case for queued-but-not-downloaded files). Here the files **had completed downloading** before the crash, so the client had full knowledge (size + checksum) that the local 0-byte content did not match what it had itself written moments earlier.

## Expected behaviour

When discovery finds `local size == 0` while the sync DB records a non-zero size **with an unchanged mtime**, the client should not silently propagate the empty file. Options:

- verify the content checksum against the DB before treating it as a local edit (the DB checksum was available: `SHA1:ed47…` vs. actual `SHA1:da39…`),
- treat it as a conflict (keep server version, create a conflict file), or
- at minimum warn, as is done for the "all files deleted" case.

A general "many files shrank to 0 bytes since last run" heuristic (analogous to the mass-deletion warning) would protect against the whole class of post-crash corruption.

## Environment

- Desktop client: 33.0.2 (Linux, Tuxedo build `mirall/33.0.2~tux1`), sync folder on ext4 (ordered)
- OS: Tuxedo OS (Ubuntu-based), kernel 6.17
- Server: Nextcloud 32.0.13, target was a group folder shared with ~dozens of users
- Trigger: amdgpu freeze → hard reset ~80 s after boot, mid-sync

## Steps to reproduce

1. Let the client fully download a batch of files during a sync run on ext4.
2. Cut power before the page cache is flushed (or `echo b > /proc/sysrq-trigger` right after the downloads finish).
3. Boot again; observe zero-length local files with preserved mtimes.
4. Client uploads the 0-byte files, overwriting server content (no conflict, no warning).

## Impact

Silent, hard-to-detect data loss for **other users' files** (group folder), recoverable only via server-side versioning. 447 files were overwritten in one sync run in our incident.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.