finishEncoding() called archive_free() and then passed the freed pointer
to wrap(), which would call archive_error_string() on it if the return
code was not ARCHIVE_OK. Fix by nil-ing the pointer before freeing and
throwing directly without attempting to read the (already freed) error
string.
ContainerizationArchive internally used a `FileArchiveWriterDelegate`
type that defined callback methods which were handed to
`archive_write_open2` from libarchive
```
int archive_write_open2(struct archive *, void *,
archive_open_callback *, archive_write_callback *,
archive_close_callback *, archive_free_callback *);
```
Turns out those methods were at the very least partly broken, when used
alongside gzip compression.
Utilities like gnutar and gunzip couldn't entirely process the archives
produced by them, leading to errors like
```
➜ gtar -tvzf rootfs.tar.gz
...
drwxr-xr-x 0/0 0 2026-03-07 11:24 sbin/
-rwxr-xr-x 0/0 122451504 2026-03-07 11:24 sbin/vminitd
-rwxr-xr-x 0/0 121138856 2026-03-07 11:24 sbin/vmexec
gzip: (stdin): trailing garbage ignored
..
gtar: Child returned status 2
gtar: Error is not recoverable: exiting now
```
libarchive provides alternate APIs, `archive_write_open_filename` and
`archive_write_open_fd` where these callbacks are defined within the
library.
Lets use those instead and let the library handle this / future edge
cases (in this particular case it was adding some padding at the end of
the gzipped compressed file)
- Closesapple/container#988.
- macOS libarchive is not built with zstd support, so the workaround in
ArchiveReader is to attempt to decompress every archive as zstd. If
decompression fails, we pass the original archive to libarchive. If it
succeeds, we pass the uncompressed archive.
- Adds blob media type recognition for zstd to EXT4Unpacker. Tested zstd
blob unpack using `image pull tonistiigi/hello-world:zstd-docker`.
Co-authored-by: Aditya Ramani <a_ramani@apple.com>