Sony path-allowlists the URI given to sceAppInstUtilInstallByPackage —
/user/data/ and /mnt/usb are accepted, a bare /data/... path is rejected with
0x80B2116F (confirmed by the ps5upload project). PatchDL stored the pkg under
/data/patchdl and passed that /data path, so every install was rejected at the
path stage. Pass the /user/data view of the same file instead (the code already
computed it as sdk_path; it was only used for AppInstallPkg before).
Also report each URI's individual rc instead of only the last attempt's, which
revealed the real wall: via file:// the installer reaches header parsing and
rejects with 0x80B21106 — the assembled file is a valid but DEBUG-magic PKG
(\x7FFIH, not retail \x7FCNT), the format Sony's system updater consumes rather
than the retail-pkg format InstallByPackage expects.
Three read-only endpoints (no install, no writes) to inspect a downloaded
package on-device:
- GET /api/manifest/<title_id> — re-fetch the patch manifest (PatchDL bypasses
the DNS block) and dump each piece's offset/size/SHA-256.
- GET /api/pkgverify/<title_id> — SHA-256 every piece of the assembled .pkg
against the manifest hashes, on-device (SSD, no multi-GB transfer), and report
per-piece pass/fail. Proves whether the file is byte-correct.
- GET /api/pkgmeta/<title_id> — read the pkg's embedded content id + title id
(GetContentIdFromPkg) vs the target ids, to expose cross-region linkage.
Supporting code: patchdl_sha256_fd_region() (pread + OpenSSL EVP) in the net
layer, and bind sceAppInstUtilGetContentIdFromPkg in the install backend.
Used to diagnose the Dead Island 2 install: the 61.6 GB package verifies
byte-perfect (17/17 pieces) and its content id matches the target, so the
0x80B2116F install rejection is a Sony install-method limitation, not the data.
Cancel a running download (the worker aborts mid-piece and the partial
file is removed) or delete a finished package, from the queue or the
title card. The progress callback now returns an abort signal that
reaches libcurl and the manifest merge loop.
Manifest merge: bound the piece scan to the "pieces" array so a later
"url" key (e.g. playgoChunkCrcUrl) can't be appended as a bogus piece,
and require each piece's fileOffset to match the bytes written so far so
an out-of-order manifest fails instead of silently producing a corrupt
package.
Report real free space on the download partition via statvfs; it was a
hardcoded 0.
Fixes found in review:
- scan: bound the SFO entry table to the bytes actually read and require
the key to be NUL-terminated before strcmp (OOB read on a crafted
param.sfo from a shadow-mounted dir).
- proc: bound the kinfo_proc walk and the name compare to the record and
the buffer.
- install: publish the API probe under the lock (data race with the MHD
worker thread) and initialize rc2.
- verxml: reject a truncated attribute value instead of returning it as
valid.
- web: keep download/install/downloaded flags across a refresh, stop the
queue poll only after repeated empty results, coerce the progress
number, and treat a cancelled download (HTTP 200, ok:false) as
not-downloaded.
Standalone PS5 payload with an embedded web UI on :12880, no etaHEN.
- Scans installed titles and classifies the source (genuine install,
ShadowMountPlus mount, preinstall, unknown) from the on-disk layout.
- Reads name, installed version and the Sony version.xml URL from the PS5
app database (vendored SQLite).
- Resolves version.xml past nanoDNS via a raw DNS query, TLS pinned to the
SCEI DNAS root.
- Filters patches to the newest one compatible with the current firmware.
- Downloads the patch and installs it through AppInstUtil (sysmodule loaded
at runtime), gated to genuine installs with a package title-id match guard.
- Action-based UI filters and an on-screen startup notification with the URL.