From fb9d06fb5ba824d06f6c13c3a225d1b512c81085 Mon Sep 17 00:00:00 2001 From: Knutwurst <36196269+knutwurst@users.noreply.github.com> Date: Wed, 24 Jun 2026 14:25:50 +0200 Subject: [PATCH] Install: pass the /user/data path Sony allowlists; report per-URI rc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/patchdl_install.c | 62 ++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/src/patchdl_install.c b/src/patchdl_install.c index 0cbfc64..3cb6f15 100644 --- a/src/patchdl_install.c +++ b/src/patchdl_install.c @@ -348,7 +348,6 @@ patchdl_install_local_pkg(const char *local_path, const char *expected_title_id, struct stat st; int rc; int pkg_tid_mismatch = 0; - const char *last_uri = ""; if (!local_path || !local_path[0]) { snprintf(msg, msg_sz, "no package path"); @@ -365,10 +364,11 @@ patchdl_install_local_pkg(const char *local_path, const char *expected_title_id, return -1; } - /* AppInstallPkg runs in a sandbox that sees the user partition as - /user/data, not /data. InstallByPackage is different: the shell/debug - installer path takes the normal /data/... URI, so keep `local_path` for - that API and use `sdk_path` only for AppInstallPkg / metadata probes. */ + /* Sony's installer sees the user partition as /user/data, not /data, and + PATH-ALLOWLISTS the URI passed to InstallByPackage: /user/data/ and + /mnt/usb are accepted, but a bare /data/... path is REJECTED with + 0x80B2116F (empirically confirmed by the ps5upload project). So feed the + /user/data view of the file to both InstallByPackage and AppInstallPkg. */ if (!strncmp(local_path, "/data/", 6)) snprintf(sdk_path, sizeof(sdk_path), "/user%s", local_path); else @@ -413,7 +413,7 @@ patchdl_install_local_pkg(const char *local_path, const char *expected_title_id, const char *title_dir; const char *file_base; - snprintf(file_uri, sizeof(file_uri), "file://%s", local_path); + snprintf(file_uri, sizeof(file_uri), "file://%s", sdk_path); title_dir = strstr(local_path, "/data/patchdl/"); file_base = strrchr(local_path, '/'); if (title_dir && file_base && file_base > title_dir + strlen("/data/patchdl/")) { @@ -433,8 +433,8 @@ patchdl_install_local_pkg(const char *local_path, const char *expected_title_id, ip, PATCHDL_HTTP_PORT, title_id, file_base + 1); } } - uris[0] = local_path; - uris[1] = file_uri; + uris[0] = sdk_path; /* /user/data/... — the allowlisted path */ + uris[1] = file_uri; /* file:///user/data/... */ uris[2] = http_loop_uri[0] ? http_loop_uri : NULL; uris[3] = http_lan_uri[0] ? http_lan_uri : NULL; meta.ex_uri = ""; @@ -443,28 +443,34 @@ patchdl_install_local_pkg(const char *local_path, const char *expected_title_id, meta.content_name = "PatchDL"; meta.icon_url = ""; - for (int i = 0; i < 4; i++) { - if (!uris[i]) continue; - memset(&pkg, 0, sizeof(pkg)); - memset(&playgo, 0, sizeof(playgo)); - meta.uri = uris[i]; - last_uri = uris[i]; - rc2 = ai_install_by_package(&meta, &pkg, &playgo); - if (rc2 == 0) { - snprintf(msg, msg_sz, "install started (InstallByPackage%s)", - pkg_tid_mismatch ? ", shared master bytes" : ""); - return 0; + { + char tries[260] = {0}; + const char *labels[4] = { "userdata", "file", "loop", "lan" }; + for (int i = 0; i < 4; i++) { + if (!uris[i]) continue; + memset(&pkg, 0, sizeof(pkg)); + memset(&playgo, 0, sizeof(playgo)); + meta.uri = uris[i]; + rc2 = ai_install_by_package(&meta, &pkg, &playgo); + { + size_t l = strlen(tries); + snprintf(tries + l, sizeof(tries) - l, "%s%s=0x%08x", + l ? "," : "", labels[i], (unsigned)rc2); + } + if (rc2 == 0) { + snprintf(msg, msg_sz, "install started (InstallByPackage%s)", + pkg_tid_mismatch ? ", shared master bytes" : ""); + return 0; + } + } + rc = rc2; + if (pkg_tid_mismatch) { + snprintf(msg, msg_sz, + "install rejected (pkg %.12s -> %.12s; tries: %s)", + pkg_tid, expected_title_id ? expected_title_id : "", tries); + return rc ? rc : -1; } } - rc = rc2; - } - - if (pkg_tid_mismatch) { - snprintf(msg, msg_sz, - "install rejected (InstallByPackage=0x%08x, pkg %.12s, target %.12s, uri %.96s)", - (unsigned)rc, pkg_tid, expected_title_id ? expected_title_id : "", - last_uri); - return rc ? rc : -1; } /* Last resort for normal same-title packages only. This path has no target