Repair corrupted zip files

Recover from CRC checksum errors, truncated files, multiple non-sequential single bit-flips, and full-byte errors in zip files.

I had several .zip files containing multiple encrypted files each that I had transferred from drive to drive over 15-20 years. When I finally got around to unzipping them, I got results like these:

>unzip -t broken.zip
 Archive: broken.zip
 testing: src/file1.ext
 error: invalid compressed data to inflate
 testing: src/file2.ext OK
 testing: src/file3.ext bad CRC 13c27231 (should be 9480df2e)
 (may instead be incorrect password)
 testing: src/file4.ext OK
 At least one error was detected in broken.zip.

There are 2 reasons that typically cause such errors:
1. Failing drive: Physical failure, such as a scratch, unrecoverable sectors, or disk corruption. This category of issues is systemic, causes large errors, and is generally unrecoverable.
2. Error accumulation: Single bit-flips may accumulate over time. Since none of my drives experienced physical failure, I gambled that this was the issue for my files.

Compressed files have minimal redundancy from which to reconstruct damaged files. Essentially, there are 3: Successful decompression, the length of the file, and a CRC checksum. The zip-repair utility brute-force searches through all possible bit-flips, validating against the available checks.

It handles:
– CRC checksum errors.
– Truncated files.
– Multiple non-sequential single bit-flips.
– Full-byte errors.

Installation:

Download the full GitHub project, or just 2 files:

>wget https://raw.githubusercontent.com/arnon-weinberg/zip-repair/master/zip-repair{,.so}
>chmod +x zip-repair

Usage:

>zip-repair broken.zip src/file1.ext

Brute-force search takes a long time, and uses a lot of memory relative to the source file size. However, uncompressed files often contain redundant information that can be used to narrow down the search. Unzip the file with errors, and then inspect it for where the damage starts. Encrypted files spew garbage from the damaged point on. If it’s a text file, then look for binary garbage.

Then, to improve performance, use the command arguments –min, –max, and –search-from:

>zip-repair broken.zip src/file1.ext --search-from 12345

If there are multiple non-sequential bit-flips, then you will have to repeat this process for each bit-flip following partial repair.

The brute-force search scans through the file in up to 3 phases. In practice, about 2 of 3 files were repaired on phase 1 (bit-flips), and the rest on phase 2 (scoring – see below); phase 3 (full-byte search) did not repair any files for me.

Using zip-repair I was able to recover 13 out of 15 corrupted files.

Automation:

If you have a lot of files, then it may be worth automating the inspection. The command arguments –min, –max, and –search-from also support custom external commands:

>zip-repair broken.zip src/file1.ext --max /path/to/external-command

The file is piped in to the external command on stdin, and the script returns a byte offset location (or -1 for file’s length). For example, for regular text files, try:

#!/bin/sh
LC_ALL=C grep -abo -P -m1 '[^\x09\x0A\x0D\x20-\x7E]' | cut -d: -f1 \
  | { read -r x; echo "${x:--1}"; }

Stored files are more complicated. One of the 3 redundancies mentioned above is successful decompression. However, if a file does not compress well, then archives often store them as-is. Ironically, compression (essentially a process of removing redundancy) actually adds redundancy relative to no compression! Without this redundancy, zip-repair cannot by itself repair multiple non-sequential single bit-flips in stored files.

However, with custom external commands, it can! When external commands are provided on the command line, they can be used to “score” partial repairs, substituting for successful decompression. Of course, garbage-in-garbage-out: Scoring is only as effective as the external commands are accurate at identifying last good / first bad locations.

For reference, I included a working example in the project repository for handling video files. Use: >zip-repair broken.zip src/file1.ext --min last_known_good.sh

Disclaimer: As I don’t expect to ever need it again, this is a throw-away script for me, and I will not be maintaining it. It was coded entirely by AI and I do not vouch for its quality or performance. I can only say it worked for me, and if you’re lucky, then maybe it will also work for you too.

Leave a Reply

Your email address will not be published. Required fields are marked *