Commit Graph
13 Commits
Author SHA1 Message Date
Knutwurst 66e4912485 Resume interrupted downloads across a reboot
An interrupted download (cancel excepted) now keeps its partial package on
disk instead of deleting it, and records the manifest it belongs to in a
sidecar (state.json). On the next start the title is flagged resumable and
the UI shows a "Paused — X downloaded" note with a Resume button.

Resume refetches the manifest, skips every piece already fully on disk, and
re-fetches only the one partially-written piece (piece-granular, no HTTP
range needed), appending the rest. The fresh-download path is unchanged. A
partial belonging to a different/older manifest is dropped and the download
starts clean; a corrupt (failed SHA-256) download is not kept.

Survives a reboot: a killed payload runs no cleanup, so the partial and its
sidecar persist under /data/patchdl until resumed, completed, or deleted.
2026-06-24 08:58:32 +02:00
Knutwurst 3f40dc1d7c Rework the web UI: tile-based progress, view nav, responsive
Replace the single-page layout with a view-based one (Games / Settings /
Logs via the left nav) and fold all download UI into the game tile — the
separate download queue is gone.

Per tile while downloading: an in-tile progress bar with auto-scaled size
(B/KB/MB/GB/TB), live transfer speed and ETA from the poll deltas, and a
green "Downloading" marker. One fixed-width action button that no longer
reflows with its label: a blue Update/Download/Install that morphs into an
amber Cancel while the download runs, plus a ghost Delete for a finished
package.

Settings and Logs moved to their own pages. Mobile-first responsive layout
(rail collapses to a top bar, icon-only nav, single-column tiles, >=44px
touch targets, 16px inputs). Real free space (statvfs) is shown auto-scaled
in the rail and status strip.

New "Home-screen shortcut" toggle (default on, persisted as home_shortcut
in config.json). The actual PS5 tile install is not wired yet: it needs a
prebuilt deeplinkUri stub PKG installed via sceAppInstUtil.

Fixes from an adversarial review pass:
- Reconcile in-flight downloads from /api/downloads onto the cards, so
  progress + Cancel appear after a reload or a download started elsewhere,
  and stop the per-poll full-grid rebuild.
- Clear the downloaded flag on install and once the server reports the
  title up to date, so a patched title no longer shows Install/Delete
  forever.
- Zero a stale speed/ETA if the byte counter goes backwards.
- a11y: nav buttons keep an accessible name when the label is hidden on
  small screens; visible focus ring on the search box; filter group is
  role=group with aria-pressed; drop the noisy grid-level aria-live; fold
  the transient "checking" state into a visible filter bucket.
v0.0.2
2026-06-23 21:48:54 +02:00
Knutwurst 5f0d1be078 Harden filesystem safety after a security review
Defense in depth around the only operations that touch the filesystem,
so no request can escape /data/patchdl or leave the process able to
write to a system path:

- Validate the HTTP title_id with path_segment_safe at the top of the
  title-action route, and again inside remove_title_dir and
  cleanup_installed_download. The delete primitives are now self-
  protecting instead of relying only on the "title exists in the scan"
  guard, so a future refactor cannot reintroduce a /data-wiping
  traversal (a title_id of ".." would otherwise resolve the dir to
  /data).
- Only swap the process root vnode when the current root was captured
  and can be restored, in both the scan and the debug dump. Otherwise
  the process could be left rooted at the system root, sending later
  absolute-path writes to the wrong place.

Reviewed and confirmed safe with no change needed: the installer only
delegates to Sony's signed AppInstUtil service (it never writes or
redirects to system paths itself), app.db is opened read-only and
immutable, every write targets /data/patchdl, the patch picker never
selects an update that needs a newer firmware, and the /api/pkg file
server already blocks path traversal.
2026-06-23 19:46:23 +02:00
Knutwurst ea1328de79 Add optional SHA-256 verification of downloaded manifest pieces
Each Sony manifest piece carries a SHA-256 (hashValue). When the new
"Verify downloaded pieces" setting is on, every piece is hashed while it
streams to disk (OpenSSL EVP, already linked) and compared against the
manifest value; a mismatch aborts the download, deletes the partial, and
reports piece_verify_failed instead of handing a corrupt 60 GB package to
the installer.

Off by default: TLS already protects the bytes in transit and the PS5
installer verifies the whole packageDigest before applying, so this is a
fail-fast belt-and-suspenders check. It is also unverified on hardware
yet, so it stays opt-in (persisted in config.json) until confirmed
on-device; the hex compare is case-insensitive since Sony mixes cases.
2026-06-23 18:08:57 +02:00
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
Knutwurst 510f199b89 Handle target-aware patch installs 2026-06-23 17:03:51 +02:00
Knutwurst 01464ae88a PatchDL 0.0.1: scan, resolve, download and install PS5 game patches
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.
v0.0.1
2026-06-23 14:52:35 +02:00
Knutwurst 4a14c689e5 Vendor web server dependency for standalone workspace 2026-06-23 08:28:20 +02:00
Knutwurst 40b26e6c1b Make PS5 payload build reproducible 2026-06-23 08:24:50 +02:00
Knutwurst 64bce0a3b5 Switch web UI to dark English interface 2026-06-23 08:17:12 +02:00
Knutwurst 56f54fc1a0 Add embedded web server skeleton 2026-06-23 08:12:29 +02:00
Knutwurst 9e89d3a342 Add source-aware update policy 2026-06-23 07:59:41 +02:00
Knutwurst 9c2822f367 Initial PatchDL web UI 2026-06-23 07:52:17 +02:00