Files
knutwurst--ps5-patchdl/web
Knutwurst 9006965a75 Add download cancel/delete and harden the patch pipeline
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.
2026-06-23 17:52:29 +02:00
..
2026-06-23 17:03:51 +02:00

PatchDL Web UI

Standalone static UI for the planned PS5 patchdl.elf web server.

Open index.html directly for the mock UI, or serve this directory from the ELF web server. The JavaScript first tries the real API and falls back to demo data when the API is not available.

The embedded ELF server serves this UI on port 12880 by default.

Expected API

GET  /api/status
GET  /api/config
POST /api/config
GET  /api/titles
GET  /api/downloads
POST /api/titles/:title_id/check
POST /api/titles/:title_id/download
POST /api/titles/:title_id/install
POST /api/titles/:title_id/enable
POST /api/titles/:title_id/disable

Policy Model

The UI assumes deny-by-default behavior:

{
  "default_policy": "deny",
  "download_dir": "/data/patchdl (internal)",
  "install_after_download": false,
  "delete_pkg_after_install": true,
  "source_policy": {
    "official": { "allow_check": true, "allow_download": true, "allow_install": true },
    "external": { "allow_check": true, "allow_download": true, "allow_install": true },
    "shadowmount": { "allow_check": true, "allow_download": true, "allow_install": false },
    "unknown": { "allow_check": true, "allow_download": false, "allow_install": false }
  },
  "cdn_allowlist": [
    "sgst.prod.dl.playstation.net",
    "gst.prod.dl.playstation.net",
    "gs2.ww.prod.dl.playstation.net"
  ]
}

Per-title modes:

disabled
download_only
latest_compatible
pin
check_only

Per-title source fields:

{
  "title_id": "PPSA90001_00",
  "name": "Shadowmounted Test Title",
  "source_type": "shadowmount",
  "source_path": "/system_ex/app/PPSA90001_00",
  "mount_from": "/mnt/usb0/itemzflow/Shadowmounted Test Title",
  "enabled": true,
  "mode": "download_only"
}

Supported source_type values:

official
external
shadowmount
unknown

The frontend treats shadowmount as download-only and unknown as blocked for downloads and installs. Backend code should enforce the same policy even if a client sends a forged request.

For PS5 game updates, the backend may turn a Sony manifest_url into a merged local .pkg by downloading all manifest pieces. The delta_url *-DP.pkg is not shown as a separate user action because it can bootstrap the storage/master title instead of the installed regional target.