Files

28 KiB

PKG Manager - Architecture & Implementation Details

This document provides an in-depth architectural guide to the PKG Manager daemon, its internal subsystems, virtual HTTP streaming engine, Direct Install upload path, PlayStation 5 system installer integration, and storage paradigms.


1. Architectural Overview

PKG Manager is designed as a persistent native daemon (pkgmgr.elf) running in userland on PlayStation 5 (Prospero OS). It couples an embedded web-based control application with an HTTP range-streaming engine. A fresh helper process calls Sony's proprietary package installer subsystem (libSceAppInstUtil.sprx) for each install.

flowchart TB
    subgraph UI ["User Interface"]
        BROWSER["<b>Web Browser / PS5 WebKit</b><br/>React Single-Page Application"]
    end

    subgraph STOR ["Media & Package Sources"]
        direction TB
        USB["<b>USB Storage</b><br/><code>/mnt/usb0-7</code>"]
        DISC["<b>Optical Disc</b><br/><code>/mnt/disc</code>"]
        SMB["<b>Network Shares</b><br/>Samba / SMB (libsmb2)"]
    end

    subgraph CTRL ["PKG Manager Control Subsystem (Port 8844)"]
        direction TB
        HTTP8844["<b>Control & Web Server</b> (:8844)<br/><code>libmicrohttpd</code> &bull; REST API & UI"]
        subgraph CTRL_WORKERS ["Daemon Subsystems"]
            direction LR
            APPINFO["<b>App Inspector</b><br/><code>app_info.c</code>"]
            INSTALLER["<b>Installation Controller</b><br/><code>installer.c</code>"]
            SCANNER["<b>Package Scanner & Parser</b><br/><code>pkg_scanner.c / pkg_parser.c</code>"]
        end
    end

    subgraph STRM ["Virtual Streaming Pipeline (Ports 18841 / 18842)"]
        direction LR
        SOCKET["<b>HTTP Range Server</b> (:18841)<br/><code>stream_server.c</code> (Raw BSD Sockets)"]
        WS["<b>Direct Install Upload</b> (:18842)<br/><code>ws_upload.c</code> (WebSocket)"]
        VSTREAM["<b>Virtual Stream Engine</b><br/><code>multipart.c</code> (Multi-part Slices & SMB)"]
    end

    subgraph PS5 ["PlayStation 5 Native System Layer"]
        direction TB
        SCE["<b>libSceAppInstUtil.sprx</b><br/><code>sceAppInstUtilInstallByPackage</code>"]
        RECEIVER["<b>PS5 Background Installer</b><br/>System Package Receiver"]
        APPDB[("<b>App Database</b><br/><code>app.db</code>")]
        NOTIF["<b>Notifications</b><br/><code>libkernel</code> (SceNotification)"]
    end

    %% UI Interaction
    BROWSER <-->|"HTTP REST / Static Assets"| HTTP8844
    BROWSER -->|"WebSocket PKG Upload"| WS

    %% Control Plane routing
    HTTP8844 -->|"Install Command"| INSTALLER
    HTTP8844 -->|"Scan Trigger"| SCANNER
    INSTALLER -->|"Check Versions"| APPINFO

    %% Left: PS5 OS interactions
    APPINFO <-->|"SQLite Query"| APPDB
    INSTALLER -->|"Embedded ELF upload :9021"| ELFLDR["elfldr"]
    ELFLDR -->|"Launch"| HELPER["pkgmgr-inst.elf"]
    INSTALLER <-->|"Loopback IPC :18843"| HELPER
    HELPER -->|"One install per process"| SCE
    INSTALLER -.->|"Disc Swap Prompts"| NOTIF
    SCE -->|"Forward Stream URL"| RECEIVER
    RECEIVER -->|"HTTP 206 Range Requests"| SOCKET

    %% Center: Stream session mount
    INSTALLER -->|"Mount Session"| VSTREAM
    SOCKET -->|"Fetch Slices"| VSTREAM
    WS -->|"RAM-backed Live Session"| VSTREAM

    %% Right: Storage reads
    STOR -->|"Scan Files & Headers"| SCANNER
    STOR -->|"pread() / smb2_read()"| VSTREAM

    %% Styling
    classDef client fill:#e3f2fd,stroke:#1565c0,stroke-width:1.5px,color:#0d47a1;
    classDef ctrl fill:#ede7f6,stroke:#512da8,stroke-width:1.5px,color:#311b92;
    classDef stor fill:#e8f5e9,stroke:#2e7d32,stroke-width:1.5px,color:#1b5e20;
    classDef strm fill:#f3e5f5,stroke:#7b1fa2,stroke-width:1.5px,color:#4a148c;
    classDef ps5 fill:#fff3e0,stroke:#e65100,stroke-width:1.5px,color:#bf360c;

    class BROWSER client;
    class HTTP8844,APPINFO,INSTALLER,SCANNER ctrl;
    class USB,DISC,SMB stor;
    class SOCKET,WS,VSTREAM strm;
    class ELFLDR,HELPER,SCE,RECEIVER,APPDB,NOTIF ps5;

2. Network Services Architecture

The daemon exposes three network services on separate TCP ports to isolate interactive UI/API traffic from package streaming and Direct Install uploads. During helper launch it also opens a loopback-only IPC listener on port 18843; elfldr is a separate service on port 9021.

Port Implementation Primary Role Features
8844 libmicrohttpd (MHD) UI & REST API Serves bundled React SPA, handles JSON REST endpoints, and serves cached package icons.
18841 Raw BSD Sockets (stream_server.c) Virtual Stream Pipeline Serves HTTP/1.1 206 Partial Content byte ranges directly to the PS5 background installer with custom socket timeouts and MSG_NOSIGNAL sends.
18842 Raw WebSocket listener (ws_upload.c) Direct Install Upload Receives PKG chunks from a LAN browser and feeds the RAM-backed live stream session used by the installer.

REST API Endpoints (Port 8844)

The primary web server handles all interactive user requests:

  • UI & Static Assets: /, /index.html, /favicon.svg, /icon.png, /cache.appcache.
  • System & Version: /api/version, /api/storage, /api/settings (GET & POST), /api/log, /api/debug.
  • Package Discovery: /api/drives, /api/packages, /api/packages/quick-scan, /api/packages/refresh, /api/scan/status.
  • Package Icons: /api/icon (extracts or serves cached icon0.png), /api/icon-error.
  • Installation Control: /api/install (starts installation), /api/cancel (aborts current installation), /api/poll / /api/status (live progress), /api/shortcut/install.
  • Direct Install Control: /api/upload/init, /api/upload/status, /api/upload/icon, /api/upload/finish, and /api/upload/cancel.
  • SMB & Metadata Cache: /api/smb/test, /api/smb/shares (enumerate shares on a server), /api/smb/browse (64 folders/PKGs per page, with an after cursor and next_cursor response), /api/smb/inspect (metadata and install eligibility for one file), /api/cache/stats, /api/cache/clear.
  • Leftovers Cleanup: /api/leftovers (scans unlinked patches/DLCs), /api/leftovers/delete (purges selected orphans).

Range Streaming Server (Port 18841)

The dedicated stream server (stream_server.c) handles package delivery to the PS5 background installer:

  • Listens on raw BSD sockets at port 18841.
  • Serves HTTP/1.1 206 Partial Content (and HEAD) responses directly via the virtual stream engine (virtual_stream_read()).
  • Uses socket timeouts (SO_RCVTIMEO/SO_SNDTIMEO) and MSG_NOSIGNAL to handle client disconnects cleanly without process interruption.
  • Session name pinning: each installation pins an exact session filename (e.g. package-<unixtime>-<seq>.pkg). Any stray or unexpected requests immediately return 404 Not Found.
  • Operates completely independently from libmicrohttpd on port 8844, preventing high-bandwidth installer range requests from degrading UI responsiveness.

3. Storage Model & Installation Execution

PKG Manager defaults to using its virtual HTTP range-streaming endpoint on port 18841 for all package sources (USB, optical disc, SMB network shares, and browser WebSocket uploads):

http://127.0.0.1:18841/stream/install/package-<unixtime>-<seq>.pkg

Streaming and Offline Installs

When the console has an active network connection, PKG Manager streams packages and shows installation progress in the app.

USB and disc packages can also be installed without a network connection. In that case, the PS5 system installer handles the package directly from storage. It does not provide progress to PKG Manager, so the app shows a status message and progress can be followed in PS5 Notifications. Cancellation is unavailable while the system installer handles the install.

If the network connection is lost during a stream install, PKG Manager can switch USB and disc installs to direct storage mode.

Key Advantages

  1. No 2x Storage Requirement: Packages do not need to be staged on internal storage before installation. The console installer extracts data directly to its target location, requiring only the space needed for the installed application itself (1x space) rather than keeping both the raw package and the installed files on disk (2x space).

  2. Full Offline Support: Consoles without any network connection can still install USB and optical disc packages without failure via the automatic fallback mechanism.

  3. Direct Installation from Network Shares (SMB): The native PS5 package installer cannot access Samba/SMB network shares directly. The virtual stream engine bridges network reads into standard HTTP range responses, enabling direct network installation without mounting shares in the OS or copying packages locally first.

  4. Direct Install from a LAN Device: A browser on a PC or other LAN device uploads a local PKG over WebSocket port 18842. The daemon keeps the upload in a bounded RAM-backed live session and exposes it through the same range-streaming path used by regular installs.

  5. Multi-Part & Optical Disc Swapping: For packages split across multiple files or optical discs (BD-R, DVD), the virtual stream translates byte offsets across parts on the fly using pread(), allowing multi-part installations without pre-reassembling the files.


4. Multi-Part Format (PS5MPKG1) & Virtual Stream Engine

The multi-part splitting format allows splitting large packages across standard optical media (BD-R, DVD) or FAT32/exFAT drives.

Header Specification (4096 bytes)

All multi-part slices share a 4KB binary header defined in include/multipart.h:

#pragma pack(push, 1)
typedef struct {
    char magic[8];               /* "PS5MPKG1" */
    uint32_t header_version;     /* 1 */
    uint32_t part_index;         /* 1-indexed (1, 2, ...) */
    uint32_t total_parts;        /* Total part count */
    uint32_t compression_type;   /* 0 = raw uncompressed sequential slice */
    uint32_t chunk_size;         /* Nominal slice block size (e.g. 2MB) */
    uint32_t num_chunks_in_part; /* 0 for uncompressed */
    uint64_t part_data_size;     /* Raw byte length of package slice in this file */
    uint64_t total_pkg_size;     /* Total size of the original uncompressed PKG */
    uint64_t total_archive_size; /* Combined byte size of all part files */
    char pkg_filename[256];      /* Original package filename */
    char title_id[32];           /* Extracted Title ID (e.g. "CUSA00000") */
    char title_name[256];        /* Application Name */
    char content_id[64];         /* Content ID */
    uint8_t package_uuid[16];    /* Unique 128-bit UUID shared by all parts */
    uint32_t icon_offset;        /* Offset of embedded icon0.png (4096 in Part 1) */
    uint32_t icon_size;          /* Length of embedded icon0.png */
    char app_version[32];        /* App version (e.g. "v01.00") */
    char pkg_type[16];           /* "base", "update", or "dlc" */
    uint64_t part_offset;        /* Offset in original package where slice starts */
    uint32_t data_offset;        /* File offset where raw package data begins */
    uint8_t reserved[3348];      /* Padding to 4096 bytes */
} multipart_header_t;
#pragma pack(pop)

Storage Strategy

Multi-part slices are stored as raw uncompressed sequential slices (compression_type = 0) to allow direct random-access byte range streaming without decompression overhead.

Optical Disc Swapping Pipeline

When a package spans across multiple optical discs:

  1. The virtual stream engine reads slices from Disc 1 (/mnt/disc).
  2. When the requested offset crosses into Part 2 (which is not mounted), virtual_stream_read() triggers the g_stream_wait_disc_fn callback.
  3. The installer state enters waiting_disc, logs the event, and issues a PS5 system notification: "Please insert Disc 2 of N for <title>"
  4. A background poll checks for optical disc remounting.
  5. As soon as Disc 2 is inserted and detected (verified via UUID and Part index match), the stream automatically opens the new part's file descriptor and resumes streaming seamlessly.

5. PlayStation 5 System Installer Integration

The application interfaces directly with the PS5 OS package manager via /system/common/lib/libSceAppInstUtil.sprx.

Function Signatures & Data Layout

typedef struct {
    char languages[30][8];
    char playgo_scenario_ids[64][3];
    char content_ids[64][48];
    unsigned char unknown[6480];
} playgo_info_t;

typedef struct pkg_info {
    char content_id[48];
    int type;
    int platform;
} pkg_info_t;

typedef struct pkg_metadata {
    const char *uri;
    const char *ex_uri;
    const char *playgo_scenario_id;
    const char *content_id;
    const char *content_name;
    const char *icon_url;
} pkg_metadata_t;

int sceAppInstUtilInitialize(void);
int sceAppInstUtilTerminate(void);
int sceAppInstUtilInstallByPackage(const pkg_metadata_t *meta, pkg_info_t *info, playgo_info_t *playgo);
int sceAppInstUtilGetInstallStatus(const char* content_id, SceAppInstallStatusInstalled* status);

Package Metadata & Installation Pipeline

Every package submission runs in a fresh pkgmgr-inst.elf helper. The helper is built separately and embedded in pkgmgr.elf. install_process.c sends it to elfldr on 127.0.0.1:9021, then accepts its callback on 127.0.0.1:18843. install_service.c exchanges fixed-size, pointer-free messages over that TCP connection; the helper owns initialization, one package submission, status polling, and termination of its AppInstUtil session. Retries and queued updates receive new processes. The daemon retains the HTTP/SMB/WebSocket servers, live upload buffers, install state, and browser connection throughout.

The upload socket is half-closed after the ELF bytes are sent so elfldr can finish loading while helper stdout remains readable. IPC deadlines, a parent-disconnect watcher, and child termination/reaping handle failed or canceled operations. Shortcut registration uses a separate helper; existing DLC queries and leftover removal retain their independent AppInstUtil client in the daemon.

When invoking sceAppInstUtilInstallByPackage, pkg_metadata_t is populated as follows:

  • uri: Defaults to a unique per-install streaming URL (http://127.0.0.1:18841/stream/install/package-<unixtime>-<seq>.pkg) for all sources. If an offline fallback occurs for a USB or disc package (0x80B21121), the direct filesystem path (e.g. /mnt/usb0/pkg/game.pkg) is passed instead. Timestamping prevents URI collisions across successive stream installations.
  • content_name: Formatted as "<Title ID> (<Kind>)" (e.g. "CUSA00000 (Base)" or "CUSA00000 (Update)").
  • content_id: Passed as an empty string ""; the system installer reads the initial package header to populate pkg_info.content_id.
  • ex_uri, playgo_scenario_id: Passed as empty strings "".
  • icon_url: The HTTP icon URL http://127.0.0.1:18841/stream/install/icon-<unixtime>-<seq>.png for stream installs when an icon is extracted, or "" for direct storage installs (preventing offline 0x80B21121 PlayGo network errors and using the internal icon0.png).

Host-Side PS5 Stream Simulator

To develop install methods (websocket client, direct stream creation) without a console, the PS5's request pattern against the stream server is reproduced on the host. The pattern, reverse-engineered from the captures in .for_reference/stream_debug/, has three phases:

  1. Header acquisition — a burst of short-lived connections, each re-reading the first 64 KiB (Range: bytes=0-65535) so the installer can parse / re-validate the package header.
  2. Sidecar CRC probe — a single Range-less GET of <content_id>.crc that must return 404 when there is no companion file.
  3. Bulk transfer — two parallel long-lived connections serving contiguous 16 MiB byte-ranges across [65536, end) in an A/B ping-pong.

Every request carries the query the console appends (?product=0287&serverIpAddr=127.0.0.1&r=00000000) against :18841.

The pure client half is tests/ps5_sim.c (raw HTTP/1.1, no dependency on the server). tests/test_stream_sim.c pairs it against the real src/stream_server.c and asserts the model holds; it is part of make test. The standalone CLI (tools/ps5_installer_sim.c, launched via tools/run_stream_sim.sh) can either start its own local server or point the same replay at any live server — exactly the shape a future websocket / remote install method needs. A correct HTTP/1.1 keep-alive reader is essential here: absorbing body bytes into the header buffer desyncs a persistent connection and makes the exact body read block on bytes the server already sent.

6. Direct-Install Upload Path

LAN browser (DirectInstallView) --ws://:18842--> ws_upload.c --RAM ring-->
virtual_stream ("live:<id>") --:18841--> system installer (submitted by fresh helper)

The browser's segmentSender.js keeps up to two segments in flight when the server advertises upload_window: 2, otherwise one. It also prepares the next file read while uploads await acknowledgement. Servers advertise demand_window in the ready reply: the browser sends the pinned header first, then waits for installer seeks. Each seek queues up to eight segments starting at the requested segment (window = min(8, max(1, RAM slots / 4))). Explicit seeks take priority; speculative queued segments are capped at twice the window, and already ACKed segments are only resent on explicit demand. Older servers without the field retain sequential upload. A ping/pong heartbeat keeps demand-mode pauses alive. Seeks for any in-flight segment are coalesced, while seeks arriving after its acknowledgement are honored because the RAM cache may have evicted it. Busy responses retry the same buffer, even when that segment was acknowledged during an earlier upload.

Debug WS_CACHE rows report cumulative byte counters, sampled at most once per second and flushed at close: duplicate_bytes counts accepted uploads already resident; reloaded_bytes counts accepted uploads previously stored but evicted; unread_evicted_bytes counts evicted segments with no bytes read during that residency. A partially read segment is not counted as unread. These distinguish cache churn from physical receive throughput without per-segment log spam.

WS_SENDER records cumulative browser timings once per second: read_wait_us is time actually awaiting file data in the sender (a prepared read usually adds almost zero); ack_latency_us sums send-to-successful-ACK latency across ack_count uploads, including transfer time. These latencies overlap with two segments in flight and are not an elapsed-time breakdown or a pure network RTT. sent_count includes retries, and window records the negotiated upload limit. Counters are scoped to the browser sender and reset on reconnect. The log flushes the latest received snapshot at close; browser reports after log closure cannot be included, so counts may omit the final acknowledgements.

src/ws_upload.c is transport only; src/ws_stream.c owns the bytes (1 MB pinned header + configurable ring, 64 MB by default, ws_live_* symbols, no load-time side effects, abort/timeout on every wait). The only touch points in existing code are additive: a live: scheme branch in virtual_stream_open/read (multipart.c), one pkg_parser_parse_mem() function reusing the in-file sub-parsers, one installer_start_live() entry plus abort/destroy hooks (installer.c), and a guarded /api/upload/ REST branch plus live: URI routing in /api/install (http_server.c). src/stream_server.c has zero diff, so the pull contract verified by test_stream_sim still holds byte-for-byte. Host proof: test_ws_stream, test_parse_mem, test_ws_upload, test_direct_install_e2e, and tools/run_direct_install_sim.sh --demo (fragmented socket push racing the standard ps5_sim pull replay, byte-exact).


7. Package Parser & Metadata Engine (pkg_parser.c)

The package parser inspects packages directly on storage media using random-access pread():

  • Format Detection:
    • \x7fCNT (PS4 standard package header): parsed directly at file offset 0.
    • \x7fFIH (PS5 native package header): outer container format. Locates the internal CNT header at offset 0x58 or by scanning FIH table entries.
    • PS5MPKG1 (Multi-part package header): parsed at offset 0.
  • Title ID & Content ID: Read directly from the CNT header (offset 0x40) or multi-part header.
  • SFO & JSON Parser: Reads param.sfo (for PS4 titles) or param.json (for PS5 native titles) from the CNT entry directory table to extract localized Title Name, Application Version, and Category.
  • Package Category Classification:
    • Update: category starting with gp, presence of playgo chunk patch (app/playgo-chunk.dat / entry 0x1008), delta patch entries (0x0407, 0x0408), or delta content type magic (0x1E, 0x41000000).
    • DLC / Extra Content: category starting with ac, al, equal to addcont, or a CNT type whose low byte is 1 when param.json does not contain the standard base-application metadata fields. This avoids treating normal base packages that share the CNT value as DLC.
    • Base App: category starting with gd, bd, gc, wt, or default fallback when no update/DLC indicators are present.
  • Icon Extraction: Locates uncompressed icon0.png data (entry type 0x1200 or named icon0.png) and serves it via /api/icon?path=....

8. Storage & Drive Scanning (pkg_scanner.c)

The scanner monitors all mount points and builds the unified catalog:

  1. Drive Discovery:
    • Checks USB mount points /mnt/usb0 through /mnt/usb7.
    • Checks optical disc mount point /mnt/disc.
    • Scans configured SMB network shares via smb_client.c.
  2. Scan Scope:
    • Regular files directly in the drive root (/mnt/usbX/*.pkg).
    • Recursive scan into the designated /pkg/ subfolder (/mnt/usbX/pkg/...).
    • Files located elsewhere on external drives are excluded to avoid scanning entire personal drives.
  3. Multi-Part Deduplication:
    • Scans and validates multi-part slices.
    • Displays only Part 1 in the UI package list with a "Disc 1 of N" badge. Secondary parts (.part2..N) are indexed internally for disc swapping but hidden from the main list.
  4. Install Capacity:
    • Reports available capacity for internal, M.2, and extended USB storage (/mnt/ext0).
    • Applies the supported storage targets when checking whether a package can be installed; PS4 packages can use internal, M.2, or extended USB storage, while PS5 packages use internal or M.2 storage.

9. Installed Application Database (app_info.c)

To prevent duplicate installations and guide the user:

  • Queries the PS5 system SQLite database at /system_data/priv/mms/app.db (table tbl_appinfo).
  • Checks base application files in /user/app/<TitleID>/ (sce_sys/param.sfo, sce_sys/param.json, eboot.bin), /system_ex/app/<TitleID>/, and external storage (/mnt/ext0/user/app/). (Note: Does not treat /user/appmeta/ as proof of base installation, as appmeta directory trees persist after game uninstallation to preserve UI icons and trophy metadata caches).
  • For DLCs, queries sceAppInstUtilGetAddcontInstalledStatus, /system_data/priv/mms/addcont.db, or checks /user/addcont/<TitleID>/.
  • UI Logic:
    • If a base package is already installed, the scanner compares the installed version with the package version:
      • If the package version is newer (installed_version < pkg->app_version), installation is allowed (can_install = 1) to support cumulative base upgrades.
      • If the installed version is the same or newer, installation is disabled with "Installed version is same or newer".
      • If version information is unavailable, installation is disabled with "Application is already installed".
    • If an update is detected, the UI verifies that the base package is installed and compares the update version against the currently installed version.

10. Manifest Caching & Quick Rescan (pkg_scanner.c)

To eliminate slow cold scans upon daemon restart and drive navigation:

  1. Manifest Persistence: The discovered catalog of drives, files, and parsed metadata is serialized to /data/pkgmgr/manifest.json (or /tmp/pkgmgr_manifest.json).
  2. Instant Warm Startup: When the daemon launches, pkg_scanner_init() checks for a valid manifest and immediately restores the full catalog without reading external media.
  3. Quick Rescan (scan_quick_single_source):
    • Gathers file names, sizes, and modification timestamps via stat() / readdir().
    • Compares the file list against recorded entries:
      • If identical: skips re-parsing entirely.
      • If modified or added: re-parses only the affected packages.
      • If removed: purges the missing entries from the in-memory catalog and manifest.
  4. Background Rescan Polling: The frontend periodically checks individual local drives. SMB and all-sources views are excluded from the 15-second timer. Startup, navigation and resume can still trigger a quick scan; browse_only shares are skipped in all scan paths.
  5. Full Scan Jobs: POST /api/packages/refresh returns HTTP 202 with {status:"accepted", started:true|false}. Admission is shared with quick scans; an active job is never queued again. Poll /api/scan/status until is_scanning is false and check failed_sources. Status becomes active before counting. SMB enumeration finishes before metadata parsing; the catalog mutex is held only for catalog access, not network reads. Browser reloads attach to status polling.
  6. Cache Invalidation: The frontend compares the backend version from /api/version with the last version it saw. After an update, it clears the local cache through /api/cache/clear and runs a full rescan. A manual cache clear also refreshes the package catalog.

11. Orphaned Leftover Detection & Cleanup (leftovers.c)

When base games are deleted, or after interrupted uninstalls, unreferenced patch or DLC data can remain on storage without being tracked by app.db:

  1. Detection:
    • Scans candidate title directories exclusively in /user/patch, /user/patch0, and /user/addcont. Candidate scanning strictly avoids /user/appmeta/ because appmeta caches trophy data for every title ever launched on the console.
    • For each discovered title ID, verifies whether the base game is actually installed on disk (in /user/app/, /system_ex/app/, /mnt/ext0/user/app/) or registered in app.db.
    • If the base game is absent, it identifies the orphaned patch, addcont, and associated appmeta directory trees for that specific title.
  2. Safety Gates:
    • Deletion is strictly blocked if the base package or title record is active in app.db or exists on disk.
    • The user is provided with exact byte sizes and paths before confirming deletion.

12. SMB Network Share Storage & Caching (smb_client.c, pkg_cache.c)

PKG Manager includes an embedded SMB2 client (libsmb2) allowing direct package installation over the local network without mounting network drives:

  1. Share Discovery & Connection: Connects to user-configured SMB shares with credentials stored in /data/pkgmgr/settings.json.
  2. Local Metadata & Icon Cache (pkg_cache.c):
    • Computes a CRC32 checksum over the package header.
    • Caches parsed metadata (meta.json) and extracted icon0.png locally on the console in /data/pkgmgr/cache/<checksum>/ (or /tmp/pkgmgr/cache/).
    • Remote SMB shares are treated as read-only; no cache files are written to network shares.
    • Subsequent scans load metadata and icons instantly from the local cache, avoiding network roundtrips.
  3. Streaming Installation: Packages on SMB shares are streamed via virtual range requests directly into the installation pipeline using smb_file_session_read(). Reads are pipelined and credit-aware to reduce network latency; ideal local-network installations have reached approximately 110 MB/s.