Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| user:rion [2026/06/10 14:45] – rion | user:rion [2026/10/05 12:04] (current) – rion | ||
|---|---|---|---|
| Line 10: | Line 10: | ||
| <WRAP tip> | <WRAP tip> | ||
| - | The Storage Manager introduced in Batocera v43 allows multiple drives to be combined into a single ROM library without requiring symbolic links, manual mounts, or custom scripts. | + | The Storage Manager introduced in Batocera v43 allows multiple |
| + | |||
| + | Advanced network/NAS and manual MergerFS setups are documented later on this page, after the Storage Manager limitations. Most users with locally attached storage do not need that configuration. | ||
| </ | </ | ||
| Line 56: | Line 58: | ||
| ===== Requirements ===== | ===== Requirements ===== | ||
| - | A drive must contain a root-level folder named: | + | For drives detected and managed automatically by the Storage Manager user interface, the drive must contain a root-level folder named: |
| < | < | ||
| Line 89: | Line 91: | ||
| </ | </ | ||
| - | The Storage Manager currently expects the '' | + | The Storage Manager |
| + | |||
| + | <WRAP info> | ||
| + | This root-level folder requirement applies to automatic Storage Manager detection. Advanced manual MergerFS pools can use other mounted directory paths, including nested local folders and network shares. | ||
| + | </ | ||
| + | |||
| + | ==== Existing disk layout and filesystems ==== | ||
| + | |||
| + | Automatic Storage Manager detection is not limited to disks that have a conventional partition table. The current backend handles both of these cases: | ||
| + | |||
| + | * A partitioned disk, where each partition is detected and mounted separately | ||
| + | * A whole disk containing a filesystem directly, with no partition table, which can be mounted as a raw filesystem | ||
| + | |||
| + | In either case, automatic merging still requires a '' | ||
| + | |||
| + | An existing partition that has no recognizable filesystem is **not** treated the same way as a completely blank disk. The automatic **Format & Merge** prompt is generated by the whole-disk empty-disk path after Batocera has checked for a partition table, partitions and a raw filesystem. Therefore, a disk containing an unusable or unformatted partition may need to be repartitioned or formatted separately before Storage Manager can use it. | ||
| ===== Using Existing Drives ===== | ===== Using Existing Drives ===== | ||
| Line 132: | Line 149: | ||
| {{wiki: | {{wiki: | ||
| + | |||
| + | ==== Filesystem choices and what formatting does ==== | ||
| + | |||
| + | The Storage Manager delegates disk formatting to '' | ||
| + | |||
| + | < | ||
| + | ext4 | ||
| + | btrfs | ||
| + | exfat | ||
| + | </ | ||
| + | |||
| + | If no filesystem type is supplied to the backend, '' | ||
| + | |||
| + | <WRAP important> | ||
| + | Formatting operates on the **whole selected disk**, not only on an existing partition. Batocera erases the beginning of the disk, creates a new GPT partition table, creates one partition using the disk, formats it, and labels the new filesystem '' | ||
| + | </ | ||
| + | |||
| + | After formatting, Batocera creates the root-level '' | ||
| + | |||
| + | Existing drives do not need to use one of the three formatting choices above if Batocera can already mount their filesystem. NTFS, for example, has explicit mount handling in the Storage Manager, but NTFS is not offered by '' | ||
| + | |||
| + | For an existing NTFS filesystem, Storage Manager currently runs '' | ||
| + | |||
| + | The backend also independently refuses a format request if the selected device resolves to Batocera' | ||
| ===== Using Multiple Drives ===== | ===== Using Multiple Drives ===== | ||
| Line 177: | Line 218: | ||
| <WRAP info> | <WRAP info> | ||
| The drive name does not matter. What matters is that the mounted drive contains a root-level '' | The drive name does not matter. What matters is that the mounted drive contains a root-level '' | ||
| + | </ | ||
| + | |||
| + | ==== Automatic detection and safety filtering ==== | ||
| + | |||
| + | The Storage Manager backend watches the common Linux block-device families used by Batocera, including '' | ||
| + | |||
| + | Among other checks, the current implementation: | ||
| + | |||
| + | * Skips the disk backing ''/ | ||
| + | * Skips EFI System, Microsoft Reserved and Windows Recovery partition GUIDs | ||
| + | * Skips partitions marked hidden or no-automount | ||
| + | * Filters many Android/ | ||
| + | * Does not offer the automatic blank-disk format prompt for disks of 4 GiB or smaller | ||
| + | * Ignores swap partitions | ||
| + | * Skips local disks already claimed by UUID-based '' | ||
| + | |||
| + | These checks are safeguards, not a substitute for verifying the disk shown by a format prompt. Always confirm the device and capacity before formatting. | ||
| + | |||
| + | <WRAP info> | ||
| + | Some of the protection checks intentionally use broad filesystem-label and partition-label matches. External media using names associated with system partitions, for example '' | ||
| + | </ | ||
| + | |||
| + | ==== Ignoring a drive ==== | ||
| + | |||
| + | The Storage Manager backend supports an '' | ||
| + | |||
| + | If an ignored drive contains a root-level '' | ||
| + | |||
| + | The ignore entry is based on a persistent **whole-disk** identifier when one is available, rather than only on a temporary ''/ | ||
| + | |||
| + | The current backend provides an '' | ||
| + | |||
| + | ==== Mount-point naming ==== | ||
| + | |||
| + | For automatically mounted partitions, Storage Manager builds a mount point under ''/ | ||
| + | |||
| + | <WRAP tip> | ||
| + | Use unique filesystem labels for ROM drives. The Storage Manager configuration stores branch paths such as ''/ | ||
| </ | </ | ||
| Line 200: | Line 279: | ||
| Games stored only on the removed drive will disappear from the library until that drive is connected and merged again. | Games stored only on the removed drive will disappear from the library until that drive is connected and merged again. | ||
| + | |||
| + | The **Eject an Extra Disk** list is generated from branches saved in '' | ||
| + | |||
| + | <WRAP important> | ||
| + | Prefer the Storage Manager eject action over physically unplugging a drive while Batocera is running. The backend has cleanup logic for an unexpected removal, but active writes cannot be made safe after the device has already disappeared. | ||
| + | </ | ||
| + | |||
| + | ==== Missing at boot, safe eject, and live unplug are different ==== | ||
| + | |||
| + | The Storage Manager keeps configured branches in '' | ||
| + | |||
| + | By contrast, using **Eject an Extra Disk** removes that branch from '' | ||
| + | |||
| + | This distinction is useful for systems where a configured drive is sometimes powered off before Batocera boots. Leaving it configured but absent at boot is different from explicitly ejecting it. | ||
| ===== What Gets Merged? ===== | ===== What Gets Merged? ===== | ||
| - | Only the ROM library | + | The graphical Storage Manager merges only the ROM library: |
| < | < | ||
| Line 221: | Line 314: | ||
| remain on internal storage. | remain on internal storage. | ||
| - | The Storage Manager does not merge BIOS files, save files, screenshots, | + | The graphical |
| + | |||
| + | Advanced manual MergerFS pools are separate from the graphical Storage Manager and can technically target other directories, | ||
| ===== Internal ROM Preservation ===== | ===== Internal ROM Preservation ===== | ||
| Line 242: | Line 337: | ||
| If all merged drives are later removed, Batocera restores the internal ROM library back to the normal ''/ | If all merged drives are later removed, Batocera restores the internal ROM library back to the normal ''/ | ||
| + | |||
| + | During restoration, | ||
| ===== Limitations ===== | ===== Limitations ===== | ||
| - | The Storage Manager currently requires this layout: | + | Automatic detection by the graphical |
| < | < | ||
| Line 261: | Line 358: | ||
| is not automatically merged. | is not automatically merged. | ||
| - | Only the ROM library | + | This limitation does not apply to advanced manual MergerFS branches, where an explicit nested path can be configured. |
| + | |||
| + | The graphical Storage Manager merges only the ROM library. Other userdata folders remain independent | ||
| + | |||
| + | <WRAP tip> | ||
| + | For most users adding locally attached USB drives, SSDs, hard drives or SD cards, the Storage Manager sections above are all that is needed. | ||
| + | |||
| + | The remaining sections are for advanced setups, manual storage configuration, | ||
| + | </ | ||
| + | |||
| + | ===== Other Storage Methods (Advanced) ===== | ||
| + | |||
| + | Batocera has several storage mechanisms that solve different problems. They should not be treated as interchangeable. | ||
| + | |||
| + | ^ Method ^ Best used for ^ What it does ^ | ||
| + | | **Storage Manager** | Multiple locally attached ROM drives | Builds a MergerFS union at ''/ | ||
| + | | **sharedevice=DEV / ANYEXTERNAL** | Moving all userdata to one selected device, or to the first suitable external device | Mounts another device as the active userdata storage | | ||
| + | | **sharedevice=DEVICES** | Mounting selected local partitions or subdirectories over specific userdata folders | Bind-mounts selected local storage over individual targets such as ROMS, SAVES or BIOS | | ||
| + | | **sharedevice=NETWORK** | Replacing all or selected userdata folders with SMB/NFS shares | Mounts network storage directly on the requested userdata paths | | ||
| + | | **Manual S12mergerfs pool** | Combining local folders and network/NAS folders in one union | Builds an advanced MergerFS pool from explicitly configured branches | | ||
| + | |||
| + | <WRAP tip> | ||
| + | If the goal is simply to add a second, third or fourth locally attached ROM drive, use the graphical Storage Manager. If the goal is to combine a NAS with local ROM storage in the same ''/ | ||
| + | </ | ||
| + | |||
| + | ===== Network/NAS Storage (Advanced) ===== | ||
| + | |||
| + | <WRAP important> | ||
| + | This section is **not required for normal Storage Manager use**. If all ROM drives are attached locally, use the graphical Storage Manager described above. | ||
| + | </ | ||
| + | |||
| + | The graphical Storage Manager currently focuses on locally attached storage devices. Batocera also includes a separate boot-time MergerFS service that can build manual pools from local storage, removable media and already-mounted network shares. | ||
| + | |||
| + | This makes it possible to present ROMs from a NAS together with ROMs stored on local drives under the same virtual directory, such as: | ||
| + | |||
| + | < | ||
| + | / | ||
| + | </ | ||
| + | |||
| + | <WRAP important> | ||
| + | Manual MergerFS pools are an advanced configuration and are not managed by the Storage Manager user interface. If you create a manual pool for ''/ | ||
| + | </ | ||
| + | |||
| + | ==== Manual MergerFS Syntax ==== | ||
| + | |||
| + | Manual pools are configured in: | ||
| + | |||
| + | < | ||
| + | / | ||
| + | </ | ||
| + | |||
| + | The basic syntax is: | ||
| + | |||
| + | < | ||
| + | mergerfs=< | ||
| + | </ | ||
| + | |||
| + | For example: | ||
| + | |||
| + | < | ||
| + | mergerfs=/ | ||
| + | </ | ||
| + | |||
| + | The path before the '' | ||
| + | |||
| + | Unlike automatic Storage Manager detection, manual branches do not need to use a root-level '' | ||
| + | |||
| + | The manual service accepts more than one pool. Keys beginning with '' | ||
| + | |||
| + | < | ||
| + | mergerfs=/ | ||
| + | mergerfs_bios=/ | ||
| + | </ | ||
| + | |||
| + | <WRAP important> | ||
| + | The manual parser uses '' | ||
| + | </ | ||
| + | |||
| + | The graphical Storage Manager key is '' | ||
| + | |||
| + | ==== Mounting a NAS Before the Pool Is Created ==== | ||
| + | |||
| + | The manual MergerFS service supports '' | ||
| + | |||
| + | Example using an SMB/CIFS guest share: | ||
| + | |||
| + | < | ||
| + | sharewait=30 | ||
| + | premerge1=mkdir -p /media/NAS && mount.cifs // | ||
| + | mergerfs=/ | ||
| + | </ | ||
| + | |||
| + | In this example: | ||
| + | |||
| + | * '' | ||
| + | * '' | ||
| + | * ''/ | ||
| + | * '' | ||
| + | |||
| + | <WRAP info> | ||
| + | The example above uses a guest SMB share for simplicity. Password-protected SMB shares and NFS mounts can also be mounted with normal Linux mount options before being included as a MergerFS branch. See the Batocera NAS documentation for the existing SMB/NFS syntax and network-storage guidance. | ||
| + | </ | ||
| + | |||
| + | Multiple '' | ||
| + | |||
| + | If '' | ||
| + | |||
| + | <WRAP important> | ||
| + | If a '' | ||
| + | |||
| + | This matters especially when the local mount-point directory already exists. If the network mount failed but a branch path still exists locally, MergerFS can see the local directory rather than the NAS. Marking a network branch '' | ||
| + | </ | ||
| + | |||
| + | <WRAP warning> | ||
| + | '' | ||
| + | </ | ||
| + | |||
| + | '' | ||
| + | |||
| + | ==== Branch Access Modes ==== | ||
| + | |||
| + | The manual MergerFS service accepts branch suffixes such as: | ||
| + | |||
| + | < | ||
| + | =RO | ||
| + | =RW | ||
| + | =NC | ||
| + | </ | ||
| + | |||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | For example: | ||
| + | |||
| + | < | ||
| + | mergerfs=/ | ||
| + | </ | ||
| + | |||
| + | allows ROMs on both branches to appear under ''/ | ||
| + | |||
| + | ==== Manual Pool File Placement ==== | ||
| + | |||
| + | The boot-time manual MergerFS service currently uses: | ||
| + | |||
| + | < | ||
| + | category.create=mfs | ||
| + | moveonenospc=true | ||
| + | minfreespace=4G | ||
| + | </ | ||
| + | |||
| + | '' | ||
| + | |||
| + | The complete option set currently passed by '' | ||
| + | |||
| + | < | ||
| + | cache.files=off | ||
| + | dropcacheonclose=false | ||
| + | category.create=mfs | ||
| + | allow_other | ||
| + | use_ino | ||
| + | moveonenospc=true | ||
| + | minfreespace=4G | ||
| + | xattr=passthrough | ||
| + | </ | ||
| + | |||
| + | When a manual pool is created, Batocera also creates a hidden local base directory derived from the pool path. For a pool at: | ||
| + | |||
| + | < | ||
| + | / | ||
| + | </ | ||
| + | |||
| + | the hidden base is: | ||
| + | |||
| + | < | ||
| + | / | ||
| + | </ | ||
| + | |||
| + | Existing files from the original pool directory are moved into this hidden base before the MergerFS mount is created. The base is then added to the pool with '' | ||
| + | |||
| + | The script also **attempts** to remount the hidden base read-only before starting MergerFS, but that command explicitly ignores failure. Do not rely on the underlying hidden base being read-only. The dependable MergerFS-side rule in the command line is '' | ||
| + | |||
| + | The service derives this hidden base path from the final component of the pool path. There is an implementation quirk worth knowing about: the manual pool **key** parser permits digits 0 through 9, but the current hidden-base derivation expression only accepts letters, '' | ||
| + | |||
| + | <WRAP important> | ||
| + | The manual '' | ||
| + | </ | ||
| + | |||
| + | <WRAP warning> | ||
| + | There is also a source-level limitation in the current '' | ||
| + | </ | ||
| + | |||
| + | ==== Relationship to sharedevice=NETWORK and sharedevice=DEVICES ==== | ||
| + | |||
| + | The existing '' | ||
| + | |||
| + | * '' | ||
| + | * '' | ||
| + | * The normal share service recognizes targets including '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | '' | ||
| + | |||
| + | The Storage Manager also deliberately skips local disks that are already referenced by UUID through manual '' | ||
| + | |||
| + | The normal '' | ||
| + | |||
| + | ==== Boot ordering and delayed Storage Manager detection ==== | ||
| + | |||
| + | Storage devices can appear very early during boot, before ''/ | ||
| + | |||
| + | At the end of '' | ||
| + | |||
| + | * Replays Storage Manager device events that were delayed until the share layer became ready | ||
| + | * Checks for ''/ | ||
| + | * Creates ''/ | ||
| + | |||
| + | This explains why an already-configured ROM drive detected during early boot can be queued first and only joined to the live pool after the base ''/ | ||
| ===== Advanced Technical Details ===== | ===== Advanced Technical Details ===== | ||
| - | Batocera uses **MergerFS** to combine ROM folders from multiple drives into a single virtual directory. | + | ==== Graphical Storage Manager Pool ==== |
| + | |||
| + | Batocera's graphical Storage Manager | ||
| The merged library is presented at: | The merged library is presented at: | ||
| Line 295: | Line 612: | ||
| </ | </ | ||
| - | ==== MergerFS | + | ==== Graphical Storage Manager |
| - | Batocera | + | The graphical Storage Manager |
| < | < | ||
| + | cache.files=partial | ||
| + | dropcacheonclose=true | ||
| category.create=eplfs | category.create=eplfs | ||
| + | allow_other | ||
| + | inodecalc=path-hash | ||
| moveonenospc=true | moveonenospc=true | ||
| minfreespace=4G | minfreespace=4G | ||
| </ | </ | ||
| + | |||
| + | When the '' | ||
| **eplfs** means: | **eplfs** means: | ||
| Line 338: | Line 661: | ||
| because it has the least free space while still meeting the minimum free space requirement. | because it has the least free space while still meeting the minimum free space requirement. | ||
| + | |||
| + | The internal '' | ||
| + | |||
| + | Because '' | ||
| <WRAP important> | <WRAP important> | ||
| - | The MergerFS implementation and file placement policy may change between Batocera releases. | + | The MergerFS implementation and file placement policy may change between Batocera releases. |
| </ | </ | ||
| + | |||
| + | ==== Important MergerFS behavior ==== | ||
| + | |||
| + | MergerFS combines directory trees. It does not stripe or split one ROM file across multiple drives. The free space shown for a pool can therefore be larger than the space available for any one file on an eligible branch. Path-preserving policies, branch modes and '' | ||
| + | |||
| + | '' | ||
| + | |||
| + | Avoid keeping duplicate files at the exact same relative path on multiple branches. MergerFS deduplicates directory entries, and its default search/ | ||
| + | |||
| + | It is possible to access the underlying branch directories directly, for example under ''/ | ||
| + | |||
| + | ===== Troubleshooting and Diagnostics ===== | ||
| + | |||
| + | The main **graphical Storage Manager backend** log is: | ||
| + | |||
| + | < | ||
| + | / | ||
| + | </ | ||
| + | |||
| + | The manual '' | ||
| + | |||
| + | Useful read-only checks from SSH include: | ||
| + | |||
| + | < | ||
| + | batocera-settings-get -f / | ||
| + | batocera-storage-manager list_ejectable | ||
| + | batocera-storage-manager list_disks | ||
| + | batocera-storage-manager list_fstypes | ||
| + | findmnt / | ||
| + | mount | grep mergerfs | ||
| + | </ | ||
| + | |||
| + | Storage Manager communicates with EmulationStation through its local HTTP API for merge/ | ||
| + | |||
| + | For the older '' | ||
| + | |||
| + | < | ||
| + | / | ||
| + | / | ||
| + | </ | ||
| + | |||
| + | If a manual NAS-backed '' | ||
| ===== Frequently Asked Questions ===== | ===== Frequently Asked Questions ===== | ||
| Line 355: | Line 724: | ||
| / | / | ||
| </ | </ | ||
| + | |||
| + | ==== Which filesystems can Storage Manager format? ==== | ||
| + | |||
| + | The implementation documented here exposes: | ||
| + | |||
| + | < | ||
| + | ext4 | ||
| + | btrfs | ||
| + | exfat | ||
| + | </ | ||
| + | |||
| + | Existing drives may use other filesystems if Batocera can mount them. NTFS is explicitly handled for existing drives, but it is not one of the Storage Manager format targets. | ||
| + | |||
| + | ==== Why does a small blank drive not produce a format prompt? ==== | ||
| + | |||
| + | The current safety logic suppresses the automatic blank-disk format prompt for disks of 4 GiB or smaller. This is intended to avoid accidentally offering small firmware/ | ||
| + | |||
| + | ==== Can I stop Storage Manager from trying to use a particular drive? ==== | ||
| + | |||
| + | Yes. The backend supports an '' | ||
| + | |||
| + | ==== How do I remove a drive from the ignore list? ==== | ||
| + | |||
| + | The current backend has an '' | ||
| + | |||
| + | ==== What if a configured drive is missing when Batocera boots? ==== | ||
| + | |||
| + | A missing configured branch is skipped from the live pool during refresh, but its saved '' | ||
| + | |||
| + | ==== Can I use a network drive or NAS in the same ROM pool? ==== | ||
| + | |||
| + | Yes, but not through the graphical Storage Manager interface. | ||
| + | |||
| + | Advanced users can mount a network share with a '' | ||
| + | |||
| + | For example: | ||
| + | |||
| + | < | ||
| + | premerge1=mkdir -p /media/NAS && mount.cifs // | ||
| + | mergerfs=/ | ||
| + | </ | ||
| + | |||
| + | See [[# | ||
| ==== What happens if I unplug a merged drive? ==== | ==== What happens if I unplug a merged drive? ==== | ||
| Line 364: | Line 776: | ||
| ==== Are BIOS files merged? ==== | ==== Are BIOS files merged? ==== | ||
| - | No. | + | Not by the graphical Storage Manager. It only manages the ROM pool at: |
| - | + | ||
| - | Only: | + | |
| < | < | ||
| Line 372: | Line 782: | ||
| </ | </ | ||
| - | is merged. | + | BIOS files normally |
| - | + | ||
| - | BIOS files remain stored in: | + | |
| < | < | ||
| / | / | ||
| </ | </ | ||
| + | |||
| + | Advanced users can separately mount or union BIOS storage with '' | ||
| ==== Are saves merged? ==== | ==== Are saves merged? ==== | ||
| - | No. | + | Not by the graphical Storage Manager. Save files remain in the normal userdata location, for example: |
| - | + | ||
| - | Save files remain | + | |
| < | < | ||
| / | / | ||
| </ | </ | ||
| + | |||
| + | Advanced '' | ||
| ==== Can I store ROMs in a different folder? ==== | ==== Can I store ROMs in a different folder? ==== | ||
| - | No. | + | For automatic detection through the graphical |
| - | + | ||
| - | The Storage Manager currently expects a root-level folder named: | + | |
| < | < | ||
| roms | roms | ||
| + | </ | ||
| + | |||
| + | For an advanced manual MergerFS pool, yes. The branch can point to an explicitly configured nested directory, for example: | ||
| + | |||
| + | < | ||
| + | / | ||
| </ | </ | ||
| Line 413: | Line 827: | ||
| ===== References ===== | ===== References ===== | ||
| - | * Batocera TV — '' | + | * Batocera TV - '' |
| * Storage Manager demonstration: | * Storage Manager demonstration: | ||
| * https:// | * https:// | ||
| Line 419: | Line 833: | ||
| * Batocera Storage Manager source code: | * Batocera Storage Manager source code: | ||
| * https:// | * https:// | ||
| + | |||
| + | * Batocera manual MergerFS boot service: | ||
| + | * https:// | ||
| + | * Source snapshot used for the original NAS/ | ||
| + | * https:// | ||
| + | |||
| + | * Batocera share service (NETWORK / DEVICES): | ||
| + | * https:// | ||
| + | |||
| + | * Batocera disk formatter: | ||
| + | * https:// | ||
| + | |||
| + | * MergerFS documentation: | ||
| + | * https:// | ||
| + | |||
| + | * Batocera NAS documentation: | ||
| + | * https:// | ||
| + | |||
| + | * Batocera external storage documentation: | ||
| + | * https:// | ||
| + | |||
| + | * Batocera changelog: | ||
| + | * https:// | ||
- user/rion.1781102705.txt.gz
- Last modified: 4 months ago
- by rion