Unpack .tar.bz2 and .tar.xz, which only half-extracted

formatOf matched .tar.gz/.tgz as a combined format and then fell through to the bare .bz2 and .xz wrappers, so a .tar.bz2 was read as one compressed file: extracting it decompressed a single layer and left a .tar to extract again, while the same archive named .tar.gz unpacked in one step. The store listing advertised "extract ZIP, TAR, TAR.GZ, GZ, BZ2, XZ and 7z", which reads as though they all behave alike.

Found by extracting one archive of every supported format through the UI on the API 35 AVD rather than trusting the suite. A real z4.tar.bz2 left z4/z4.tar - raw ustar headers and all - beside z3.tar.gz fully unpacked to z3/a.txt and z3/sub/b.txt.

TAR_BZIP2 and TAR_XZ now sit alongside TAR_GZIP, detection consumes the whole tar suffix (.tar.bz2/.tbz2, .tar.xz/.txz) before the bare compressor, and the engine gains the two readArchive branches. The bare spellings still mean a single compressed file.

The suite had a gap that let this hide: only ZIP, GZIP, XZ and 7z had round-trip tests, so neither plain TAR nor TAR_GZIP did, and there was nothing for the asymmetry to contradict. All four tar spellings have one now, built in memory with the same library that reads them. 212 JVM tests, lint clean.

Also records the format sweep in EDGE_CASE_TESTING and re-measures the release artefacts (aab 4 838 235 / sha256 e1d23130..., apk 2 155 455 / sha256 37fb46cb...). The APK byte count happened to land on the same figure as the previous build, so the new literals were confirmed present in the shipped dex rather than trusted to the size.
This commit is contained in:
2026-10-15 12:14:39 +02:00
parent 9c39f15aa8
commit cd0fdff31b
8 changed files with 183 additions and 17 deletions
+18 -5
View File
@@ -66,6 +66,7 @@ track to report back.
| The progress notification | **Fixed, then verified.** After the fix, a real 220 MB copy posts notification `1001` on channel `file-operations` with `ONGOING_EVENT\|NO_CLEAR\|FOREGROUND_SERVICE` and `category=progress`, then removes it and stops the service when the copy finishes. Before the fix the app's notification importance was `NONE`. |
| Copy to completion | The staged copy landed at `/sdcard/bigfile.bin`, byte-count identical, via the overflow menu's "Paste here". |
| Compress and extract from the UI | **Found a bug, fixed.** Long-pressing a folder and choosing "Compress to ZIP" produced a `tocompress.zip` that the host's `unzip` read back with the right relative paths and contents. Selecting that `.zip` then exposed the selection bar clipping its own actions — see below. |
| One archive of **every** supported format | **Found a bug, fixed.** A real `.zip`, `.7z`, `.tar`, `.tar.gz`, `.tar.bz2`, `.txt.xz`, `.txt.gz` and `.txt.bz2` were built on the host and all eight extracted through the UI. Each landed with the right entry names and byte-for-byte content — except `.tar.bz2`, which produced a bare `.tar`, recorded below. |
| Long-press from adb | `input swipe x y x y 700` on a single point *did* open the selection bar and reach Copy. The same gesture has been flaky on the MIUI phone, so it still deserves a manual pass. |
### The bugs this session found
@@ -100,6 +101,16 @@ everything at once, which is honest but not ideal — moving the two least-used
Clear selection) into the overflow menu would be the better fix, and is left as deliberate
follow-up rather than bundled in here.
**`.tar.bz2` and `.tar.xz` only half-extracted.** `ArchiveFormats.formatOf` tested `.tar.gz`/`.tgz`
first, as a combined format, but then matched `.bz2` and `.xz` *before* it ever considered the
`.tar` in the middle of the name — so a `.tar.gz` unpacked to its files while a `.tar.bz2` was
read as one compressed file and yielded a `.tar`. Driving the sweep made it exact: extracting
`z4.tar.bz2` left `z4/z4.tar`, raw `ustar` headers and all, beside `z3.tar.gz`'s fully unpacked
`z3/a.txt` and `z3/sub/b.txt`. `TAR_BZIP2` and `TAR_XZ` now exist alongside `TAR_GZIP`, and
detection consumes the whole tar suffix before the bare compressor. The suite could not have
caught this: only ZIP, GZIP, XZ and 7z had round-trip tests, so neither plain `TAR` nor
`TAR_GZIP` did, and there was nothing for the asymmetry to contradict. All four have one now.
The lesson is the one the phone already taught twice: a green test suite says the code does
what the tests describe, not that the app does anything on a device. Both times the gap was a
user-visible promise the code no longer kept — once in a dialog, once in a permission.
@@ -118,8 +129,10 @@ user-visible promise the code no longer kept — once in a dialog, once in a per
- **The foreground-service notification** during a long copy. **Now observed** on the API 35 AVD
(see above), including its teardown — so what is left unverified is a *physical* device, where
the system may defer or kill a background `dataSync` service on its own schedule.
- **Archive creation/extraction against real files.** Now driven end-to-end on the API 35 AVD: a
folder of three files compressed to a `.zip` that the host's `unzip` read back with the right
relative paths and contents, and that `.zip` then selected for extraction. What is still
untested is the exotic end of the format list — 7z, `.xz`, the tar wrappers — against real
files rather than the fixtures the JVM tests build.
- **Archive creation/extraction against real files.** Driven end-to-end on the API 35 AVD, one
archive of every supported format: `ZIP`, `7z`, `TAR`, `TAR.GZ`, `TAR.BZ2`, `TAR.XZ`, `GZ`,
`BZ2` and `XZ` all extracted through the UI with the right entry names and byte-for-byte
contents, and a folder compressed to a `.zip` that the host's `unzip` read back. That sweep is
what found the `.tar.bz2` asymmetry fixed above. Still untested: a *hostile* archive on a
device — the zip-slip and 7z-slip cases are JVM-only — and RAR/CAB/ISO/WIM, which the app
refuses by design.