4.0.x -> 4.1.x resume incompatibility can remap and corrupt torrents containing zero-length files
What is the issue?
Note
This report follows an extended investigation of real data-integrity incidents. Multiple pre-patch .resume files were decoded and compared with their immutable metainfo, affected files were checked by size, inode, and content, and the relevant resume, file-I/O, and verification paths were traced in Transmission 4.1.3.
Summary
Transmission 4.0.x omitted zero-length files while parsing metainfo. PR #6232, merged as commit ed4fad9b, added zero-length file support by removing this early return from torrent-metainfo.cc:
if (file_length_ == 0)
{
return ok;
}This correctly made zero-length files visible in 4.1.x, but also changed the file indexes of every following file. No migration was added for the per-file arrays already stored in 4.0.x .resume files.
For a torrent with N metainfo files and K zero-length files, 4.0.x saved files, dnd, priority, and progress.mtimes arrays with N - K entries. The same metainfo has N file entries in 4.1.x.
The DND and priority loaders reject an incompatible list length, while mtimes only affect verification trust. load_filenames(), however, accepts the shorter legacy list and applies every available pathname positionally. It is therefore the only per-file loader that applies an incompatible list positionally instead of rejecting or explicitly handling the length mismatch. All paths after an omitted zero-length file are assigned to the wrong metainfo indexes.
This is a deterministic 4.0.x -> 4.1.x state-compatibility regression, not a randomly malformed resume file.
Reproduction
Add this multi-file torrent to Transmission 4.0.6:
0: root/a 1: root/zero (length 0) 2: root/b 3: root/cLet 4.0.6 save its resume data. Because it omits the zero-length metainfo entry, the stored filename list is:
root/a root/b root/cStart an unpatched 4.1.x client with the same profile.
load_filenames()applies those entries to indexes 0, 1, and 2. Index 3 keeps its canonical path, so indexes 2 and 3 both resolve toroot/c.If the torrent runs, different logical files can read or write through the shifted mapping. If 4.1.x saves the resume, the filename list becomes full-length but internally inconsistent:
root/a root/b root/c root/c
The short primary form proves the 4.0.x layout. After an unpatched 4.1.x save, that cross-array signature is lost and the shifted paths may no longer be distinguishable from legitimate user renames.
Field evidence
Four independent .torrent / pre-patch .resume pairs from a 4.0.x installation had exactly the layout predicted by the regression:
| Case | Metainfo files | Zero-length indexes | files / dnd / priority / mtimes lengths |
|---|---|---|---|
| A | 20 | 2 | 19 / 19 / 19 / 19 |
| B | 2,549 | 0 | 2,548 / 2,548 / 2,548 / 2,548 |
| C | 433 | 251 | 432 / 432 / 432 / 432 |
| D | 4,532 | 3,034 | 4,531 / 4,531 / 4,531 / 4,531 |
In every case, the stored path sequence matched the metainfo sequence with the zero-length entry omitted. Case B confirms the boundary case where index 0 is omitted and every stored pathname is shifted.
For case A, both generations were captured: a backup held the original 19-entry form, while the live resume had already been rewritten by unpatched 4.1.x into the full-length, internally inconsistent form. All paths after the zero-length placeholder were shifted and the final path was duplicated. Two file indexes addressed the same final file. Two adjacent files on disk were also confirmed byte-for-byte identical even though the metainfo described different files.
Consequences and seedbox impact
Transmission's open-file cache is keyed by (torrent id, file index), not by canonical path. Different file indexes that resolve to the same pathname may therefore obtain separate file descriptors for the same inode and write unrelated torrent data at their respective local offsets.
The resulting consequences include data written into the wrong files, duplicated or corrupted files, invalid uploads, large progress drops during verification, .part renames reflecting the newly calculated incomplete state, re-downloads, and substantial manual recovery work.
This regression was discovered on a seedbox containing roughly 14,000 torrents, where many affected torrents were found. The same failure pattern was also observed on other servers. Every multi-file torrent containing a zero-length file whose resume was written by 4.0.x carries the incompatible per-file layout. In a large long-running session, the incompatible mapping may go unnoticed. Loading and saving the torrent under 4.1.x can already persist the shifted mapping, while data corruption or invalid uploads occur when the torrent performs file I/O; verification may only expose the damage afterward. Inspecting or verifying the entire session manually is impractical.
Verification by itself does not fix the problem: it cannot reconstruct the shifted filename mapping or restore physical files and data already written under the wrong indexes. If it runs before the layout is repaired, it can cause large progress drops and unnecessary re-downloads.
#8791 describes a closely matching upgrade from 4.0.6 to 4.1.1: changed file structure and sizes followed by a 100%-to-0% drop on verification.
Also potentially related: #8959 reports an upgrade from 4.0.5 to 4.1.2 with old resume files, zero-length files, and Transmission treating their paths as if they had been replaced by other files from the torrent.
A solution has already been developed and tested on high-load servers and will be proposed shortly.
Which application of Transmission?
None
Which version of Transmission?
No response
Source: transmission/transmission