Compare commits

..
14 Commits
Author SHA1 Message Date
Roman Tarasov 851c269647 v 1.6beta2 Stabilization of registration
Wait for /dev/lvd1 to be freed to avoid remounting after closing y2bj
The number of game registration attempts has been increased to 2x
The backport is mounted only after successful registration to ensure its successful completion. If you delete a game with a backport, re-registration will only occur after a reboot.
2026-02-27 01:07:49 +03:00
Roman Tarasov c9b3415048 Fixed instant game mounting 2026-02-25 04:05:03 +03:00
Roman Tarasov 11b32a97cf 1.6test2 Restored nullfs mounting 2026-02-23 23:50:10 +03:00
Roman Tarasov a43401391d 1.6test1
Added backport support for images from /data/backports/<TitleID>.
Image mount folder layout has been standardized to /data/imgmnt/<FS>mnt/<filename>
2026-02-23 20:44:07 +03:00
Roman Tarasov 0f7bc24928 Add "force_mount" option 2026-02-23 02:35:29 +03:00
Roman Tarasov 1db534cf82 Update readme 2026-02-23 01:50:05 +03:00
Roman Tarasov 3f81ae589f 1.5beta6 Refactor image mount flow, add per-image RO/RW overrides, and improve mount error reporting
- Rename mount base from /data/ufsmnt to /data/imgmnt
- Add per-image mount mode overrides via config:  image_ro=<filename>, image_rw=<filename>
- Improve mount failure notifications.
- Optimize stale-link recovery path.
2026-02-23 01:16:35 +03:00
Roman Tarasov 32ae0c9c50 Improve startup debug logging flow 2026-02-22 13:35:15 +03:00
Roman Tarasov 5564b4396d v. 1.5beta5 Improve mount lifecycle recovery and link cleanup
Added stale mount.lnk cleanup logic for /user/app/* entries, including broken links and invalid sources.
Added automatic remount recovery: if a previously mounted image becomes inactive while the image file still exists, retry mounting.
Added optional cleanup of related /system_ex/app/<TITLE_ID> nullfs bind mounts when unmounting an image source.
Added debug log rotation on startup
2026-02-22 12:52:49 +03:00
Roman Tarasov abe6bbb92b Merge branch 'main' of https://github.com/drakmor/ShadowMountPlus 2026-02-22 02:36:28 +03:00
Roman Tarasov 896e5af0fd Fix RO/RW mount flag mapping and kstuff loader mount-link handling 2026-02-22 02:35:36 +03:00
Roman Tarasov 15776edb8b Update README.md 2026-02-21 12:30:10 +03:00
Roman Tarasov 1bc6477ea7 Fixing exFAT image builds in Windows 2026-02-21 06:16:56 +03:00
Roman Tarasov eb2a4c1036 Fix source stability checks to avoid false wait/skip loops 2026-02-21 05:50:15 +03:00
5 changed files with 1166 additions and 589 deletions

No files matched your search

+16 -15
View File
@@ -1,21 +1,22 @@
PS5_PAYLOAD_SDK ?= /opt/ps5-payload-sdk
include $(PS5_PAYLOAD_SDK)/toolchain/prospero.mk
# Standard Flags (No extra libraries)
PS5_PAYLOAD_SDK ?= /opt/ps5-payload-sdk
include $(PS5_PAYLOAD_SDK)/toolchain/prospero.mk
# Standard Flags (No extra libraries)
CFLAGS := -O2 -Wall -D_BSD_SOURCE -std=gnu11 -Isrc
# Linker
LDFLAGS :=
# Standard Libraries Only
# Standard Libraries Only
LIBS := -lkernel_sys -lSceSystemService -lSceUserService -lSceAppInstUtil -lsqlite3
# Targets
all: shadowmountplus.elf
# Build Daemon
shadowmountplus.elf: src/main.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS)
clean:
# Targets
all: shadowmountplus.elf
# Build Daemon
shadowmountplus.elf: src/main.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS)
$(PS5_PAYLOAD_SDK)/bin/prospero-strip --strip-debug $@
clean:
rm -f shadowmountplus.elf kill.elf src/*.o
+16 -35
View File
@@ -3,7 +3,7 @@
PURPOSE
-------
Create a RAW image file, mount it via OSFMount as a logical volume,
auto-select FAT32/exFAT by image size, and either:
format it as exFAT, and either:
- format + copy + dismount (default), or
- create + mount only for manual steps.
@@ -45,11 +45,10 @@
NOTES
-----
- This script does NOT auto-elevate. Start PowerShell as Administrator.
- FS selection is automatic by image size:
* < 4 GB -> FAT32, cluster 65536
* >= 4 GB -> exFAT, cluster auto-selected:
- large-file sets: 65536
- small/mixed-file sets: 32768
- Filesystem is always exFAT.
- Cluster size is auto-selected:
- large-file sets: 65536
- small/mixed-file sets: 32768
#>
[CmdletBinding()]
@@ -89,9 +88,9 @@ function Find-OSFMountCom {
if ($cmd) { return $cmd.Source }
$candidates = @(
"$env:ProgramFiles\OSFMount\osfmount.com",
"${env:ProgramFiles}\OSFMount\osfmount.com",
"${env:ProgramFiles(x86)}\OSFMount\osfmount.com",
"$env:ProgramFiles\PassMark\OSFMount\osfmount.com",
"${env:ProgramFiles}\PassMark\OSFMount\osfmount.com",
"${env:ProgramFiles(x86)}\PassMark\OSFMount\osfmount.com"
) | Where-Object { $_ -and (Test-Path $_) }
@@ -245,11 +244,9 @@ function Dismount-OsfVolume([string]$osfPath, [string]$mountPoint, [int]$maxAtte
return $false
}
function Invoke-FormatVolume([string]$driveLetter, [string]$fileSystem, [int]$clusterSize, [string]$label) {
function Invoke-FormatVolume([string]$driveLetter, [int]$clusterSize, [string]$label) {
$target = "${driveLetter}:"
if ($fileSystem -notin @("FAT32", "exFAT")) {
throw "Unsupported file system '$fileSystem'. Expected FAT32 or exFAT."
}
$fileSystem = "exFAT"
$clusterArg = Format-AllocationUnitArg -clusterSize $clusterSize
$attempts = @(
@{ Name = "$fileSystem quick with requested allocation unit"; Args = @($target, "/FS:$fileSystem", "/A:$clusterArg", "/Q", "/V:$label", "/X", "/Y") }
@@ -311,40 +308,23 @@ if (Test-Path $ImagePath) {
[Int64]$expectedBytes = 0
[string]$osfSizeArg = $null
[Int64]$FsSwitchThresholdBytes = 4GB
[int]$Fat32ClusterSize = 65536
[int]$ExfatClusterSize = 65536
[int]$ExfatClusterSize = Get-OptimalExfatClusterSize -dir $SourceDir
[bool]$sizeProvided = -not [string]::IsNullOrWhiteSpace($Size)
[string]$TargetFs = ""
[int]$TargetClusterSize = 0
[int]$TargetClusterSize = $ExfatClusterSize
if (-not $sizeProvided) {
Write-Host "[Info] Size not provided. Computing an optimal image size from '$SourceDir'..."
$fat32Candidate = Get-OptimalImageSizeBytes -dir $SourceDir -clusterBytes $Fat32ClusterSize
if ($fat32Candidate -lt $FsSwitchThresholdBytes) {
$expectedBytes = $fat32Candidate
} else {
$ExfatClusterSize = Get-OptimalExfatClusterSize -dir $SourceDir
$expectedBytes = Get-OptimalImageSizeBytes -dir $SourceDir -clusterBytes $ExfatClusterSize
}
$expectedBytes = Get-OptimalImageSizeBytes -dir $SourceDir -clusterBytes $TargetClusterSize
$osfSizeArg = "$expectedBytes"
} else {
$expectedBytes = Parse-SizeToBytes $Size
$osfSizeArg = $Size
}
if ($expectedBytes -lt $FsSwitchThresholdBytes) {
$TargetFs = "FAT32"
$TargetClusterSize = $Fat32ClusterSize
} else {
$TargetFs = "exFAT"
$ExfatClusterSize = Get-OptimalExfatClusterSize -dir $SourceDir
$TargetClusterSize = $ExfatClusterSize
}
if (-not $sizeProvided) {
Write-Host "[Info] Computed image size: $(Format-Bytes $expectedBytes) ($expectedBytes bytes)."
}
Write-Host "[Info] Selected filesystem: $TargetFs (cluster=$TargetClusterSize) for image size $(Format-Bytes $expectedBytes)."
Write-Host "[Info] Selected filesystem: exFAT (cluster=$TargetClusterSize) for image size $(Format-Bytes $expectedBytes)."
$osf = Find-OSFMountCom
@@ -380,8 +360,8 @@ try {
return
}
Write-Host "[2/4] Formatting $dest as $TargetFs (cluster=$TargetClusterSize, label='$Label') via format.com ..."
Invoke-FormatVolume -driveLetter $DriveLetter -fileSystem $TargetFs -clusterSize $TargetClusterSize -label $Label
Write-Host "[2/4] Formatting $dest as exFAT (cluster=$TargetClusterSize, label='$Label') via format.com ..."
Invoke-FormatVolume -driveLetter $DriveLetter -clusterSize $TargetClusterSize -label $Label
if (-not (Test-Path $dest)) { throw "Drive $dest is not accessible after formatting." }
@@ -390,6 +370,7 @@ try {
$SourceDir, $dest,
"/E", "/COPY:DAT", "/DCOPY:DAT",
"/R:1", "/W:1",
"/NFL", "/NDL",
"/ETA"
)
& robocopy.exe @roboArgs
+48 -15
View File
@@ -1,9 +1,11 @@
# ShadowMountPlus (PS5)
**Version:** `1.5beta3`
**Version:** `1.6beta1`
**Repository:** https://github.com/drakmor/shadowMountPlus
**Warning! Mounting images can cause shutdown problems and data corruption on internal drives! This depends on many factors, but is more common with older firmware versions. Please take this into account when testing.**
**ShadowMountPlus** is a fully automated, background "Auto-Mounter" payload for Jailbroken PlayStation 5 consoles. It streamlines the game mounting process by eliminating the need for manual configuration or external tools (such as DumpRunner or Itemzflow). ShadowMountPlus automatically detects, mounts, and installs game dumps from both **internal and external storage**.
@@ -17,19 +19,19 @@
| Extension | Mounted FS | Attach backend | Status |
| --- | --- | --- | --- |
| `.exfat` | `exfatfs` | `LVD` or `MD` (configurable) | Stable |
| `.ffpkg` | `ufs` | `LVD` or `MD` (configurable) | High performance |
| `.exfat` | `exfatfs` | `LVD` or `MD` (configurable) | Optimal |
| `.ffpkg` | `ufs` | `LVD` or `MD` (configurable) | Legacy |
| `.ffpfs` | `pfs` | `LVD` | Experimental |
Notes:
- Backend, read-only mode, and sector size can be configured via `/data/shadowmount/config.ini`.
- Debug logging is enabled by default (`debug=1`) and writes to console plus `/data/shadowmount/debug.log` (set `debug=0` to disable).
- **On 4.xx firmware, shutting down the console may not work correctly when using image files.**
- **exFAT is the preferred image filesystem, including on 4.xx firmware, with no known reboot/shutdown issues in typical use.**
## Recommended FS choice
- Prefer **UFS (`.ffpkg`)** for games that work correctly on UFS: it is generally more performant (~1.5-2.5 x exFAT).
- Use **exFAT (`.exfat`)** for games that only work from external-disk style layouts, because exFAT is case-insensitive.
- Prefer **exFAT (`.exfat`)** in most cases: it is generally more performant, case-insensitive, and does not have reboot/shutdown issues in typical use.
- Use **UFS (`.ffpkg`)** only when specifically needed for compatibility with your game/setup.
## Runtime config (`/data/shadowmount/config.ini`)
@@ -38,7 +40,13 @@ This file is optional. If it does not exist, built-in defaults are used.
Supported keys (all optional):
- `debug=1|0` (`1` enables `log_debug` output to console + `/data/shadowmount/debug.log`; default is `1`)
- `mount_read_only=1|0` (default: `1`)
- `force_mount=1|0` (mounting even damaged file systems; default: `0`)
- `image_ro=<image_filename>` (repeatable; force read-only mode for this image filename)
- `image_rw=<image_filename>` (repeatable; force read-write mode for this image filename)
- `recursive_scan=1|0` (`0` = scan only first-level subfolders, `1` = recursive scan without depth limit; default: `0`)
- `backports_path=<absolute_path>` (default: `/data/backports`; if `<backports_path>/<TITLE_ID>` exists, it is overlaid on mounted image via `unionfs`)
- `scan_interval_seconds=<1..3600>` (full scan loop interval; default: `10`)
- `stability_wait_seconds=<0..3600>` (minimum source age before processing; default: `10`)
- `exfat_backend=lvd|md` (default: `lvd`)
- `ufs_backend=lvd|md` (default: `lvd`)
- `scanpath=<absolute_path>` (can be repeated on multiple lines; default: built-in scan path list below)
@@ -48,12 +56,30 @@ Supported keys (all optional):
- `md_exfat_sector_size=<value>` (default: `512`)
- `md_ufs_sector_size=<value>` (default: `512`)
Per-image mode override behavior:
- Match is done by image file name (without path).
- File names with spaces are supported.
- If multiple rules target the same file name, the last one in config wins.
- If no rule matches, global `mount_read_only` is used.
- Example:
```ini
mount_read_only=1
image_rw=PPSA1234-my-image.ffpfs
image_rw=MYGame 123.exfat
image_ro=legacy_dump.ffpkg
```
Scan path behavior:
- If at least one `scanpath=...` is present, only those custom paths are used.
- `/data/ufsmnt` is always added automatically, even with custom paths.
- `/data/imgmnt/{ufsmnt,exfatmnt,pfsmnt}` are always added automatically, even with custom paths.
- With `recursive_scan=0` (default), only first-level subfolders are checked.
- With `recursive_scan=1`, subfolders are scanned recursively.
- Full scan loop runs every 10 seconds.
- Full scan loop runs every `scan_interval_seconds` (default: `10`).
- Sources newer than `stability_wait_seconds` are deferred until stable (default: `10`).
Backport overlay behavior:
- If `${backports_path}/${TITLE_ID}` exists, ShadowMount+ mounts it over the game root with `unionfs`.
- Files from backport folder override files from the mounted image.
Validation:
- See `config.ini.example` for a ready-to-use template.
@@ -62,7 +88,9 @@ Validation:
Image mountpoints are created under:
`/data/ufsmnt/<image_name>-<fs_suffix>`
`/data/imgmnt/<fs>/<image_name>`
Where `<fs>` is one of: `ufsmnt`, `exfatmnt`, `pfsmnt`.
Image layout requirement (`.ffpkg`, `.exfat`, `.ffpfs`):
- Game files must be placed at the image root.
@@ -84,7 +112,7 @@ Default scan locations:
- `/mnt/usb0` .. `/mnt/usb7`
- `/mnt/ext0`
- `/mnt/ext1`
- `/data/ufsmnt` (mounted image content scan)
- `/data/imgmnt/ufsmnt`, `/data/imgmnt/exfatmnt`, `/data/imgmnt/pfsmnt` (mounted image content scan)
You can override scan roots with `scanpath=...` entries in `/data/shadowmount/config.ini`.
@@ -148,7 +176,9 @@ FreeBSD:
Windows:
- You can create UFS2 images with **UFS2Tool** https://github.com/SvenGDK/UFS2Tool.
- Recommended output format for this project is `.ffpkg`.
- Example:
- `UFS2Tool.exe newfs -O 2 -b 32768 -f 4096 -m 0 -i 262144 -D ./APPXXXX ./PPSA12345.ffpkg`
## Installation and usage
@@ -167,11 +197,13 @@ Add ShadowMountPlus to your `autoload.txt` for **plk-autoloader** to ensure it s
**Sample Configuration:**
```ini
!1000
kstuff.elf
!1000
notify.elf ; Optional - Remove this line if you do not want Rich Toasts
!1000
shadowmountplus.elf
!3000
ps5-backpork.elf
!1000
kstuff.elf
```
---
@@ -183,14 +215,15 @@ If a game is not mounted:
- Check `/data/shadowmount/debug.log` and system notifications from ShadowMount+.
- Verify scan roots:
- if `scanpath=...` is set, only these paths are scanned;
- `/data/ufsmnt` is always scanned.
- `/data/imgmnt/{ufsmnt,exfatmnt,pfsmnt}` are always scanned.
- Verify scan depth:
- `recursive_scan=0` scans only first-level subfolders;
- `recursive_scan=1` scans recursively.
- If logs show `source not stable yet`, adjust `stability_wait_seconds` (or wait for source copy/write to finish).
- Verify game structure:
- folder game: `<GAME_DIR>/sce_sys/param.json`;
- image game (`.ffpkg` / `.exfat` / `.ffpfs`): `sce_sys/param.json` must be at image root (no extra top-level folder).
- If you see `missing/invalid param.json` for an image, check via FTP that `/data/ufsmnt/<TITLE_ID>/` contains full game files and `sce_sys/param.json`.
- If you see `missing/invalid param.json` for an image, check via FTP that files are present under `/data/imgmnt/<fs>/<image_name>/` and include `sce_sys/param.json`.
- If you see image mount failure, check image integrity and filesystem type (`.ffpkg`=UFS, `.exfat`=exFAT, `.ffpfs`=PFS).
- If you see duplicate titleId notification, keep only one source per `<TITLE_ID>`.
+23 -1
View File
@@ -14,11 +14,33 @@ debug=1
# 0/false/no/off/rw -> read-write mounts
mount_read_only=1
# Mounting even damaged file systems
# 1/true/yes/on -> include force option
# 0/false/no/off -> default mount options (default)
force_mount=0
# Per-image mount mode override by file name (can be repeated many times):
# image_ro=<image_filename>
# image_rw=<image_filename>
# image_ro=PPSA12345.ffpfs
# image_rw=My Game Dump.exfat
# Recursive scan mode:
# 1/true/yes/on -> scan subdirectories recursively (no depth limit)
# 0/false/no/off -> scan only first-level subdirectories (default)
recursive_scan=0
# Optional backport overlay root:
# If "<backports_path>/<TITLE_ID>" exists, it is mounted over image root via unionfs.
backports_path=/data/backports
# Full scan loop interval (seconds), range: 1..3600
scan_interval_seconds=10
# Source stabilization wait window (seconds), range: 0..3600
# If source mtime is newer than this window, it will be deferred to next scan.
stability_wait_seconds=10
# Backend selection per filesystem:
# lvd -> /dev/lvdctl -> /dev/lvdN
# md -> /dev/mdctl -> /dev/mdN
@@ -34,6 +56,6 @@ ufs_backend=lvd
# Optional custom scan roots (can be repeated):
# If at least one scanpath=... is present, only these paths are used.
# "/data/ufsmnt" is always added automatically.
# "/data/imgmnt/{ufsmnt,exfatmnt,pfsmnt}" are always added automatically.
# scanpath=/data/homebrew
# scanpath=/data/etaHEN/games
+1063 -523
View File
File diff suppressed because it is too large. Load diff