lolelffs (LOL ELF FileSystem) is a Linux kernel filesystem module that enables creating fully functional filesystems within ELF binary files. Based on simplefs and compliant with the ELF specification, it allows a single binary to serve as both an executable program and a mountable filesystem.
- Overview
- Use Cases
- Features
- Architecture
- Building
- Usage
- Rust CLI Tools
- Testing and Benchmarks
- Technical Details
- Performance Considerations
- Limitations
- Contributing
- License
- References
lolelffs embeds a complete filesystem within a special ELF section (.lolfs.super), allowing you to:
- Create filesystems on regular image files or ELF binaries
- Mount and use them like any standard Linux filesystem
- Distribute self-contained binaries with embedded data
- Use modern Rust CLI tools for filesystem manipulation without kernel module overhead
The magic number 0x101E1FF5 encodes "lolelffs" in hexspeak, reflecting the project's playful origins while providing serious filesystem functionality.
Package an application with all its configuration files, assets, and data in a single executable:
# Create your application binary
gcc -o myapp main.c
# Embed a filesystem with configuration and assets
truncate -s 10M myapp
./mkfs.lolelffs myapp
# Mount and add files
sudo mount -t lolelffs -o loop myapp /mnt/lolelffs
sudo cp -r config/ assets/ /mnt/lolelffs/
sudo umount /mnt/lolelffs
# Distribute the single binary containing everythingBenefits: Single-file deployment, no external dependencies, versioned assets bundled with code.
Ideal for resource-constrained environments where a full filesystem stack is too heavy:
- Single-binary firmware: Embed configuration, certificates, and initial data
- Read-only data storage: Ship devices with pre-configured filesystems
- Reduced attack surface: No need for complex filesystem drivers
- Predictable memory footprint: Fixed block sizes and extent-based allocation
Bootstrap containers with embedded initialization data:
# Create an init binary with embedded filesystem
cp /bin/busybox init
truncate -s 5M init
./mkfs.lolelffs init
# Add initialization scripts and config
lolelffs write -i init /init.sh -c '#!/bin/sh
echo "Initializing container..."
exec /app'Learn filesystem internals with a simple, well-documented implementation:
- Filesystem design: Study extent-based allocation, inode management, bitmap tracking
- Kernel module development: Understand VFS integration and kernel APIs
- ELF format exploration: See how custom sections can extend binary functionality
- Systems programming: Bridge userspace tools (Rust) with kernel modules (C)
Embed sensitive configuration or secrets within executables:
- Certificates and keys for TLS applications
- License files for commercial software
- Encrypted configuration data
- API tokens and credentials (for development/testing)
Create self-contained development tools with embedded resources:
# Bundle a compiler with its standard library
cp /usr/bin/gcc my-gcc
truncate -s 50M my-gcc
./mkfs.lolelffs my-gcc
# Add headers, libraries, etc.Create interesting reverse engineering challenges:
- Hide flags or data within filesystem structures
- Create multi-layer challenges (execute + mount + explore)
- Study filesystem forensics techniques
Create portable filesystem snapshots:
# Create a backup archive
dd if=/dev/zero of=backup.img bs=1M count=500
./mkfs.lolelffs backup.img
# Use Rust tools to add files without mounting
lolelffs cp -i backup.img /path/to/important/files /backup/Build applications that load functionality from embedded filesystems:
- Game engines with embedded asset filesystems
- IDEs with bundled plugins and themes
- Media players with embedded codec libraries
Create reproducible test environments:
# Create test fixtures embedded in test binary
./mkfs.lolelffs test_runner
lolelffs mkdir -i test_runner /fixtures -p
lolelffs write -i test_runner /fixtures/test_data.json -c '{"test": true}'| Feature | Description | Limits |
|---|---|---|
| ELF-compliant | Filesystems can exist within valid ELF binaries | N/A |
| Extent-based allocation | Efficient storage with contiguous block ranges | Up to 170 extents per file |
| Large extent support | Extents up to 2GB for uncompressed files | 524,288 blocks per extent |
| Sparse metadata | Metadata blocks only allocated when needed | Reduces overhead |
| POSIX operations | Files, directories, hard links, symbolic links | Full support |
| Permissions | Standard Unix mode, uid, gid | rwx for user/group/other |
| Timestamps | atime, mtime, ctime | Unix seconds |
| Metric | Value |
|---|---|
| Block size | 4 KB (fixed) |
| Maximum file size | ~347 GB (uncompressed), ~1.33 GB (mixed compression) |
| Maximum filename | 255 characters |
| Maximum files per directory | 40,920 |
| Inodes per block | 56 |
| Blocks per extent (uncompressed) | 524,288 (2 GB) |
| Blocks per extent (with metadata) | 2,048 (8 MB) |
| Max extents per file | 170 |
- Kernel module: Full VFS integration for mounting
- mkfs utility: Create filesystems with ELF detection
- fsck utility: Filesystem consistency checking
- Rust CLI tools: Complete userspace filesystem manipulation
lolelffs/
├── Kernel Module (C)
│ ├── fs.c # Module init/mount/unmount
│ ├── super.c # Superblock operations
│ ├── inode.c # Inode caching & VFS integration
│ ├── file.c # File read/write operations
│ ├── dir.c # Directory operations
│ ├── extent.c # Extent search (binary search optimized)
│ ├── bitmap.h # Bitmap manipulation
│ └── lolelffs.h # Core data structures
│
├── Utilities (C)
│ ├── mkfs.c # Create filesystems
│ └── fsck.lolelffs.c # Filesystem checker
│
├── Rust CLI Tools
│ └── lolelffs-tools/
│ └── src/
│ ├── main.rs # 15+ CLI commands
│ ├── fs.rs # Core filesystem operations
│ ├── types.rs # Data structures
│ ├── dir.rs # Directory operations
│ ├── file.rs # File operations
│ ├── bitmap.rs # Allocation management
│ └── lib.rs # Library exports
│
└── Tests
└── test/
├── test_unit.c # Structure validation
├── test_mkfs.c # Creation tests
├── test_benchmark.c # Performance tests
├── test_stress.c # Edge case testing
└── test.sh # Integration tests
+---------------+
| superblock | 1 block (4 KB)
+---------------+
| inode store | sb->nr_istore_blocks
+---------------+
| ifree bitmap | sb->nr_ifree_blocks
+---------------+
| bfree bitmap | sb->nr_bfree_blocks
+---------------+
| data |
| blocks | remaining blocks
+---------------+
- Linux kernel headers
- libelf development library
- GCC
- Rust toolchain (for CLI tools)
Debian/Ubuntu:
sudo apt-get install build-essential linux-headers-$(uname -r) libelf-dev
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shFedora/RHEL:
sudo dnf install kernel-devel elfutils-libelf-devel gcc make
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shArch Linux:
sudo pacman -S linux-headers libelf base-devel rust# Build everything (kernel module, mkfs, Rust tools)
make
# Build only the kernel module and mkfs
make all
# Build only the Rust CLI tools
make rust-tools
# Build Rust tools in debug mode
make rust-tools-debug
# Clean build artifacts
make cleanBuild outputs:
lolelffs.ko- Kernel modulemkfs.lolelffs- Filesystem creation utilityfsck.lolelffs- Filesystem checkerlolelffs-tools/target/release/lolelffs- Rust CLI
On a regular file:
# Create a 100MB filesystem image
dd if=/dev/zero of=myfs.img bs=1M count=100
./mkfs.lolelffs myfs.imgOn an ELF binary:
# Copy an existing binary
cp /bin/ls myelf.bin
# Pad to minimum size (must be at least 400KB)
truncate -s 1M myelf.bin
# Create filesystem in the binary
./mkfs.lolelffs myelf.bin# Load the kernel module
sudo insmod lolelffs.ko
# Create mount point and mount
mkdir -p /mnt/lolelffs
sudo mount -t lolelffs -o loop myfs.img /mnt/lolelffs
# Use the filesystem
sudo cp file.txt /mnt/lolelffs/
ls /mnt/lolelffs/
# Unmount when done
sudo umount /mnt/lolelffs
sudo rmmod lolelffs./fsck.lolelffs myfs.imgThe Rust CLI provides complete filesystem manipulation without requiring the kernel module. This is ideal for development, scripting, and environments where kernel modules cannot be loaded.
# Build and install to PATH
make rust-tools
sudo cp lolelffs-tools/target/release/lolelffs /usr/local/bin/# Show superblock information
lolelffs super -i image.img
# Show filesystem usage (like df)
lolelffs df -i image.img
lolelffs df -i image.img -H # Human-readable sizes# List directory contents
lolelffs ls -i image.img /
lolelffs ls -i image.img / -l # Long format
lolelffs ls -i image.img / -a # Show all (including . and ..)
# Read file contents
lolelffs cat -i image.img /path/to/file.txt
# Write content to a file
lolelffs write -i image.img /file.txt -c "Hello, World!"
# Copy file from host to filesystem
lolelffs cp -i image.img /host/path/file.txt /fs/path/file.txt
# Extract file from filesystem to host
lolelffs extract -i image.img /fs/path/file.txt /host/destination/
# Get file/directory information
lolelffs stat -i image.img /path/to/file# Create directory
lolelffs mkdir -i image.img /newdir
# Create nested directories
lolelffs mkdir -i image.img /path/to/nested/dir -p
# Remove file or empty directory
lolelffs rm -i image.img /path/to/file# Create hard link
lolelffs ln -i image.img /target /link
# Create symbolic link
lolelffs ln -i image.img /target /link -s# Create a new filesystem
lolelffs mkfs --size 100M output.img
# Create with specific block count
lolelffs mkfs --blocks 25600 output.img# Create a new filesystem
lolelffs mkfs --size 50M myfs.img
# Create directory structure
lolelffs mkdir -i myfs.img /config -p
lolelffs mkdir -i myfs.img /data -p
lolelffs mkdir -i myfs.img /logs -p
# Add configuration file
lolelffs write -i myfs.img /config/app.conf -c "
server_port=8080
log_level=info
data_dir=/data
"
# Copy data files from host
lolelffs cp -i myfs.img ~/project/data/initial.json /data/initial.json
# Verify contents
lolelffs ls -i myfs.img / -l
lolelffs cat -i myfs.img /config/app.conf
lolelffs df -i myfs.img -HRun tests that don't require root:
make testTests included:
test_unit- Validates constants, structures, and calculationstest_mkfs- Tests filesystem creation utility
make benchmarkMeasures:
- Extent search performance with binary search
- Bitmap operations throughput
- Directory entry calculations
- Memory layout efficiency
make stressTests edge cases:
- Large file extent patterns
- Maximum directory capacity
- Pathological access patterns
- Full extent tree traversal
Requires root for kernel module operations:
make test-integrationmake test-allmake test-imageCreates a 200MB test filesystem at test.img.
struct lolelffs_sb_info {
uint32_t magic; // 0x101E1FF5
uint32_t nr_blocks; // Total blocks
uint32_t nr_inodes; // Total inodes
uint32_t nr_istore_blocks; // Inode store blocks
uint32_t nr_ifree_blocks; // Inode bitmap blocks
uint32_t nr_bfree_blocks; // Block bitmap blocks
uint32_t nr_free_inodes; // Available inodes
uint32_t nr_free_blocks; // Available blocks
// Compression support
uint32_t version; // Filesystem version (always 1)
uint32_t comp_default_algo; // Default compression algorithm
uint32_t comp_enabled; // Compression enabled flag
uint32_t comp_min_block_size; // Min block size for compression
uint32_t comp_features; // Feature flags
uint32_t max_extent_blocks; // Max blocks per extent (2048)
// Encryption support
uint32_t enc_enabled; // Encryption enabled flag
uint32_t enc_default_algo; // Default encryption algorithm
uint32_t enc_kdf_algo; // Key derivation function
uint32_t enc_kdf_iterations; // KDF iterations
uint32_t enc_kdf_memory; // KDF memory cost (KB)
uint32_t enc_kdf_parallelism; // KDF parallelism
uint8_t enc_salt[32]; // Salt for key derivation
uint8_t enc_master_key[32]; // Encrypted master key
uint32_t enc_features; // Feature flags
uint32_t reserved[3]; // Reserved for future use
};struct lolelffs_inode {
uint32_t i_mode; // File type + permissions
uint32_t i_uid; // Owner user ID
uint32_t i_gid; // Owner group ID
uint32_t i_size; // Size in bytes
uint32_t i_ctime; // Change time
uint32_t i_atime; // Access time
uint32_t i_mtime; // Modification time
uint32_t i_blocks; // Block count
uint32_t i_nlink; // Hard link count
uint32_t ei_block; // Extent index block
uint32_t xattr_block; // Extended attributes block (0 = none)
char i_data[28]; // Symlink target (max 27 chars + NUL)
};struct lolelffs_extent {
uint32_t ee_block; // First logical block number
uint32_t ee_len; // Number of blocks in extent
uint32_t ee_start; // First physical block number
uint16_t ee_comp_algo; // Compression algorithm for extent
uint8_t ee_enc_algo; // Encryption algorithm for extent
uint8_t ee_reserved; // Reserved for alignment
uint16_t ee_flags; // Flags (compressed, encrypted, etc.)
uint16_t ee_reserved2; // Reserved for alignment
uint32_t ee_meta; // Block number of metadata
};lolelffs supports two extent size limits depending on compression metadata requirements:
Large Extents (No Per-Block Metadata):
- Used for uncompressed files or files with uniform compression
- Maximum size: 524,288 blocks (2 GB)
- No metadata block allocation needed
ee_meta = 0andLOLELFFS_EXT_HAS_METAflag not set- Enables files up to ~347 GB
Standard Extents (With Per-Block Metadata):
- Used for mixed compression (different algorithms per block)
- Maximum size: 2,048 blocks (8 MB)
- Metadata block allocated at
ee_metablock number LOLELFFS_EXT_HAS_METAflag set- Enables granular per-block compression control
The filesystem automatically chooses the appropriate extent type based on compression requirements. Currently, per-block metadata is not used, so all extents use the large extent format.
struct lolelffs_file {
uint32_t inode; // Inode number
char filename[255]; // Null-terminated filename
};BLOCK_SIZE = 4096 bytes
INODES_PER_BLOCK = 4096 / 72 = 56
MAX_EXTENTS = (4096 - 4) / 24 = 170
MAX_BLOCKS_PER_EXTENT_LARGE = 524,288 (2 GB per extent)
MAX_FILESIZE = 524,288 × 4096 × 170 = 368,050,700,288 bytes (~347 GB)
MAX_BLOCKS_PER_EXTENT = 2,048 (limited by metadata block)
MAX_FILESIZE = 2,048 × 4096 × 170 = 1,426,063,360 bytes (~1.33 GB)
METADATA_CAPACITY = 2,040 blocks per 4KB metadata block
FILES_PER_BLOCK = 4096 / 259 = 15
MAX_FILES_PER_DIR = 15 × 170 × 524,288 = 1,335,705,600
When creating a filesystem on an ELF binary, mkfs:
- Validates the ELF magic number
- Parses the ELF header to find the end of program data
- Places the filesystem after the ELF content
- Creates a
.lolfs.supersection reference - The binary remains executable while also being mountable
The extent search uses binary search with locality hints:
- O(log n) lookup for random access
- O(1) for sequential access with hint caching
- Optimized for both large files and many small extents
- Greedy allocation attempts to extend existing extents
- Bitmap scanning for free block discovery
- Consecutive block preference for new extents
- Kernel module uses slab caching for inodes
- Rust tools use memory-mapped I/O where beneficial
- Fixed-size structures enable predictable memory footprint
| Image Size | Recommended Use |
|---|---|
| < 1 MB | Configuration files, small data |
| 1-10 MB | Application assets, embedded data |
| 10-100 MB | Development environments, test fixtures |
| 100+ MB | Archives, large datasets |
- Block size: Fixed at 4 KB (cannot be changed)
- Maximum file size: ~347 GB for uncompressed files, ~1.33 GB for files with per-block mixed compression
- Maximum extent count: 170 extents per file (limited by 4KB extent index block)
- Metadata block capacity: 2,040 blocks per metadata block (limits mixed-compression extent size)
- No journaling: Not crash-safe
- Single-threaded mkfs: Large images take time to create
- No resize: Cannot grow or shrink existing filesystems
Contributions are welcome! Areas of interest:
- Add journaling for crash recovery
- Implement file compression (zlib, lz4)
- Add encryption support
- Support extended attributes
- Implement filesystem resize
- Add FUSE support for non-root mounting
# Clone the repository
git clone https://github.com/hodgesds/lolelffs.git
cd lolelffs
# Build in debug mode
make rust-tools-debug
# Run tests
make test-all
# Create test filesystem for experimentation
make test-image- C code follows Linux kernel style
- Rust code uses standard rustfmt formatting
- All new features should include tests
- Unit tests for new functionality
- Integration tests for kernel module changes
- Benchmark tests for performance-sensitive code
Dual BSD/GPL
Daniel Hodges