How Magisk's Module System Uses Bind Mounts and tmpfs to Modify Read-Only Partitions
Magisk overlays module files onto read-only system partitions by staging them in a temporary tmpfs filesystem and applying layered bind mounts, allowing runtime modifications without altering the underlying partition image.
The Magisk module system—implemented in the topjohnwu/Magisk repository—provides a mechanism for Android users to modify system files without permanently flashing changes to read-only partitions like /system or /vendor. By leveraging Linux kernel mount primitives, Magisk creates writable overlays that mask original files while preserving the read-only nature of the underlying storage.
The Core Mechanism: tmpfs Staging and Bind Mount Overlays
Magisk's approach relies on a three-phase mounting strategy that separates file preparation from the final overlay application. This ensures that modules can supply new files while the system continues to view the mount points as read-only, maintaining Android's security model.
Creating the Temporary Staging Area
Before applying any overlays, Magisk establishes a volatile workspace in RAM. The installer script defines TMPDIR="/dev/magisk_tmp" and mounts it as a tmpfs filesystem with restricted permissions. This temporary space holds module files during the installation process, ensuring that the subsequent bind operations source from a writable location rather than the original module storage.
According to scripts/module_installer.sh, the setup occurs at lines 30-34:
TMPDIR="/dev/magisk_tmp"
mkdir -p "$TMPDIR"
# Make TMPDIR a tmpfs like /
mount -t tmpfs tmpfs "$TMPDIR" -o mode=755
This tmpfs mount provides a blank slate with 755 permissions where module payloads can be staged without affecting permanent storage.
Parsing module.prop for Mount Points
Modules declare their intended file replacements through entries in the module.prop file. Each line follows a target=source syntax, where the target represents the absolute path on the read-only partition (e.g., /system/app/Example.apk) and the source represents the relative path within the module directory.
The installer processes these mappings through a parsing loop located at lines 39-52 of scripts/module_installer.sh:
while IFS= read -r line || [ -n "$line" ]; do
[ -z "$line" ] && continue
case "$line" in "#"*) continue;; esac
target=${line%%=*}
source=${line#*=}
src="${TMPDIR}/${source}"
dst="${target}"
bind_mount "$src" "$dst"
done < "$MODDIR/module.prop"
This loop skips comments and empty lines, extracting the destination path and the corresponding source file from the tmpfs staging area for each bind mount operation.
The Three-Step Bind Mount Process
For each file replacement specified in module.prop, Magisk executes the bind_mount() helper function defined at lines 11-16 of scripts/module_installer.sh. This function implements a layered mounting strategy that preserves read-only semantics while enabling file overrides:
Step 1: Mask the original location with a writable tmpfs placeholder.
Step 2: Bind mount the module's source file from the staging area onto the destination.
Step 3: Remount the bind as read-only to match the original partition's access restrictions.
The implementation appears as follows:
bind_mount() {
src=$1 dst=$2
mount -t tmpfs tmpfs "$dst" -o mode=755
mount -o bind "$src" "$dst"
mount -o bind,ro "$src" "$dst"
}
This technique allows the system to see the module's file at the original path while maintaining the read-only flag that Android's security framework expects.
Implementation Details in module_installer.sh
The scripts/module_installer.sh file serves as the primary engine for module installation. After establishing the tmpfs staging area and parsing the configuration, it handles cleanup to ensure no temporary mounts persist unnecessarily.
Following the bind mount operations, the script removes the temporary infrastructure at lines 54-56:
umount_recursive "${TMPDIR}"
rmdir "${TMPDIR}"
The umount_recursive helper (defined earlier in the script) ensures that all nested mounts within the temporary directory are properly detached before the directory itself is deleted, preventing mount namespace pollution.
File Structure and Key Components
Understanding the module system's architecture requires familiarity with these specific files in the topjohnwu/Magisk repository:
scripts/module_installer.sh– Contains the core logic fortmpfscreation,module.propparsing, and thebind_mount()implementation that performs the overlay operations.scripts/live_setup.sh– Implements similar temporary mounting logic for live boot scenarios, establishing the foundation for runtime module application.docs/guides.md– Documents themodule.propformat and the expected syntax for defining mount point mappings between module sources and system targets.
Summary
- Magisk modules modify read-only partitions without flashing by using bind mounts layered over tmpfs placeholders.
- The system creates a temporary
tmpfsat/dev/magisk_tmpto stage module files before applying them to the system. - The
bind_mount()function inscripts/module_installer.shapplies a three-step mount process: tmpfs masking, bind mounting the source file, and remounting as read-only. - Configuration occurs through
module.propentries usingtarget=sourcesyntax, parsed by the installer's main loop. - Temporary mounts are cleaned up after installation, leaving only the persistent bind overlays active.
Frequently Asked Questions
Why does Magisk use tmpfs instead of directly bind mounting from the module directory?
Magisk copies files to a tmpfs staging area before bind mounting because the original module directory may reside on storage that becomes unavailable during certain boot stages or lacks the appropriate mount propagation. The tmpfs at /dev/magisk_tmp guarantees a writable, always-accessible source for bind operations regardless of the underlying filesystem state.
How does Magisk handle read-only restrictions on system partitions?
Rather than attempting to remount entire partitions as read-write—which would trigger Android's verified boot security mechanisms—Magisk overlays individual files using bind mounts. The bind_mount() function specifically applies mount -o bind,ro as its final step, ensuring that each overridden file maintains read-only permissions even though it originates from a writable tmpfs source.
What happens to the tmpfs after the module is installed?
The temporary tmpfs is unmounted and removed immediately after all bind mounts are established. The umount_recursive function detaches the staging directory at /dev/magisk_tmp, leaving the module files accessible only through their bind-mounted locations on the system partitions. This minimizes memory usage and eliminates temporary file exposure.
Can multiple modules override the same system file?
Later bind mounts mask earlier ones in the mount namespace hierarchy. If two modules target the same system path, the module processed last during the installation sequence will have its bind mount visible at that location. Magisk's module loading order determines which file ultimately appears on the system, with subsequent mounts superseding previous overlays.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →