Skip to content

itms-services / OTA installs broken by raw ZIP copy in #506 (regression of #469 / #433) #540

Description

@iosconstantine

Summary

PR #506 (v2.5.0) switched replicateZip to OpenRaw/CreateRaw. Apps installed
over the air (itms-services / MDM / OTA) then fail to install — a regression of
#469, which fixed exactly this for #433.

Symptom

The produced .ipa installs fine over USB (ideviceinstaller) but an OTA
install reaches ~100% and then iOS demotes the app to a placeholder ("cloud")
icon. Device syslog:

SZExtractorErrorDomain Code=3 "Finishing an incomplete stream! This stream will not be resumable"
  -[StreamingUnzipState finishStream]

USB works because ideviceinstaller unpacks only after the whole file has
landed; itms-services streams and unzips as it downloads.

What differs (measured)

Running the same App Store base through both replicateZip implementations and
parsing the local file headers:

local file header Apple original CreateRaw (#506) CreateHeader (#469)
general-purpose bit 3 clear clear set
CRC / sizes inline yes yes zero (deferred)
trailing data descriptor none none present

CreateRaw output is byte-identical to Apple's original (a verbatim copy).
CreateHeader recompresses each entry and re-frames it with a trailing
data-descriptor record (PK\x07\x08, general-purpose bit 3 set).

Observed behaviour

Only the CreateHeader (recompressed / data-descriptor) form installs over the
air; the verbatim CreateRaw form fails with the syslog above. App Store IPAs are
never OTA-installed as-is — the tool injects the sinf and repackages first, and
that repackage historically used CreateHeader, so the inline form first reached
the OTA path with #506, which is when OTA installs broke.

Most likely cause

iOS's OTA streaming installer (SZExtractor / StreamingUnzip) reads
front-to-back without the central directory and appears to finalize each entry on
the trailing data descriptor that only the recompress path emits; the
descriptor-less verbatim form is left unterminated (Finishing an incomplete stream). I can't fully isolate this from the recompression itself —
CreateHeader changes both the framing and the compressed bytes — so this is the
observed-effect explanation, not a proven mechanism.

Fix

Restore the Open/CreateHeader path (as #469). The cost is CPU to recompress;
if that regression matters, the raw fast path could be kept behind an opt-in flag
with CreateHeader as the OTA-safe default.

A branch with the fix + a unit test (asserts a Deflate entry is re-framed with a
data descriptor and round-trips; go test ./... passes) is here, since PRs are
limited to collaborators:
https://github.com/iosconstantine/ipatool/tree/fix/itms-ota-raw-zip

Refs: #433, #469, #506.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions