What are SquashFS Read-Only Filesystems-Best Embedded Linux Training Online

SquashFS Read-Only Filesystems

A free Linux kernel development course lesson on building compact, tamper-resistant read-only root filesystems

Chapter 7 · Storage
Lesson 19
~16 min read

Every embedded product eventually asks the same question: what happens if the power dies mid-write, or an attacker tries to tamper with the root filesystem? One of the strongest answers available on Linux is to simply never let those files be writable in the first place. This lesson in our free Linux kernel development course covers squashfs, the compressed read-only filesystem that makes this practical, and explains exactly how it fits into a NAND or NOR flash storage strategy.

Keywords covered in this lesson

squashfs mksquashfs read-only filesystem CONFIG_MTD_UBI_BLOCK bad block awareness compression ratio

What You Will Learn

  • Why compressed, read-only filesystems achieve better compression ratios than JFFS2/UBIFS for static content
  • How squashfs’s block-based compression and inode table are organized on disk
  • How to build and deploy a squashfs image with the latest mksquashfs
  • Why squashfs cannot be used directly on raw NAND, and how UBI’s MTD block emulation solves that
  • How to update a squashfs-based partition safely in the field

Prerequisites

This lesson builds on the UBI and MTD lessons earlier in this free Linux kernel development course. You should already know how to create UBI volumes with ubiformat/ubimkvol and be comfortable with basic MTD partitioning.

Why Read-Only Compressed Filesystems Exist

JFFS2 and UBIFS both compress data on the fly, and both are perfectly capable of storing a root filesystem. But their compression has to coexist with the possibility that any file might be rewritten tomorrow, so both filesystems keep bookkeeping structures — erase-block summaries, journal nodes, wandering trees — that exist purely to support future writes. If your root filesystem is never going to change after the image is built, all of that bookkeeping is pure overhead.

A read-only filesystem can drop that overhead entirely and spend the saved space on tighter compression. Historically Linux offered three such filesystems — romfs, cramfs, and squashfs. The first two are considered obsolete today: romfs has no compression at all, and cramfs’s block size and file size limits are too restrictive for modern use. squashfs is the only one still actively maintained and used in production, from embedded routers to live Linux distribution ISOs.

How SquashFS Is Structured

squashfs was originally written by Phillip Lougher and merged into mainline Linux in version 2.6.29. Internally, a squashfs image is organized as a small set of tightly packed tables: a superblock, a compressed inode table, a compressed directory table, a fragment table for small files, and the compressed data blocks themselves.

Regular files are split into fixed-size data blocks (commonly 128 KiB) and each block is compressed independently, which lets the kernel decompress only the blocks it actually needs to read rather than an entire file. Files smaller than one block are packed together into shared “fragment” blocks so that many small files do not each waste a partial block — this is one of the main reasons squashfs images end up so much smaller than an equivalent tar.gz.

SquashFS on-disk layout
+——————-+ | Superblock | compression type, block size, counts +——————-+ | Compressed | | inode table | file metadata, permissions, sizes +——————-+ | Compressed | | directory table | directory entries -> inode numbers +——————-+ | Fragment table | packs many small files into shared blocks +——————-+ | Compressed data | | blocks (128 KiB | | each, independent) | +——————-+

Building A SquashFS Image

The workflow is a single command: point mksquashfs at a staged root filesystem tree and it produces one image file. On the latest squashfs-tools you can also choose the compression algorithm — zstd typically gives the best balance of ratio and decompression speed for embedded CPUs today, ahead of the older default gzip.

# build a squashfs image using zstd compression
mksquashfs rootfs/ rootfs.squashfs -comp zstd -Xcompression-level 19

# inspect the resulting image without mounting it
unsquashfs -l rootfs.squashfs

# compare sizes against the uncompressed tree
du -sh rootfs/ rootfs.squashfs

A squashfs filesystem is inherently read-only: there is no write path in the kernel driver at all, which is a deliberate design choice rather than a missing feature. The only way to “update” a squashfs partition is to erase it and program a brand-new image — which, as you will see in the next lesson, is exactly the property you want for a robust A/B update scheme.

Using SquashFS On Raw NAND: The MTD Block Layer

Here is the detail that catches people out: squashfs, like ext4 and F2FS, is not bad-block-aware. It assumes every block it addresses is reliable. That is true for NOR flash and for managed flash such as eMMC, but raw NAND chips ship with factory bad blocks and can develop more over their lifetime, so you cannot mount squashfs directly on a raw MTD device.

The fix is to put UBI in between. UBI already maps out bad blocks and presents a reliable logical volume; enabling CONFIG_MTD_UBI_BLOCK lets the kernel expose any UBI volume as a virtual, bad-block-free block device (for example /dev/ubiblock0_0), and squashfs can be mounted on top of that block device without ever seeing the underlying NAND’s imperfections.

SquashFS over UBI on raw NAND
NAND flash chip (has bad blocks) | v MTD partition (raw, bad-block-aware) | v UBI volume (wear leveling, bad block mapping) | v CONFIG_MTD_UBI_BLOCK -> /dev/ubiblock0_0 | v squashfs (read-only, NOT bad-block-aware)
# create a UBI volume sized for the squashfs image
ubimkvol /dev/ubi0 -N rootfs -s 32MiB

# expose it as a read-only block device
echo "ubi0_0" > /sys/class/ubi/ubi0_0/dev

# mount squashfs on top of the emulated block device
mount -t squashfs /dev/ubiblock0_0 /mnt/rootfs

A Small Demo: Verifying A SquashFS Mount From A Kernel Module

To close the loop, here is a minimal original driver, ep_squashfs_probe, that reads the magic number directly from an MTD/UBI block device to confirm it holds a valid squashfs image before your init scripts attempt to mount it — a useful sanity check on boards where a corrupted update could otherwise hang early boot.

// ep_squashfs_probe.c - checks the squashfs magic before mount
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/blkdev.h>

#define SQUASHFS_MAGIC 0x73717368

static int __init ep_squashfs_probe_init(void)
{
    struct file *f;
    u32 magic = 0;
    loff_t pos = 0;
    ssize_t n;

    f = filp_open("/dev/ubiblock0_0", O_RDONLY, 0);
    if (IS_ERR(f)) {
        pr_err("ep_squashfs_probe: cannot open block device\n");
        return PTR_ERR(f);
    }

    n = kernel_read(f, &magic, sizeof(magic), &pos);
    filp_close(f, NULL);

    if (n != sizeof(magic) || magic != SQUASHFS_MAGIC) {
        pr_err("ep_squashfs_probe: bad squashfs magic 0x%08x\n", magic);
        return -EINVAL;
    }

    pr_info("ep_squashfs_probe: valid squashfs image detected\n");
    return 0;
}

module_init(ep_squashfs_probe_init);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala demo: verify squashfs magic before mounting");
$ insmod ep_squashfs_probe.ko
$ dmesg | tail -1
[    3.881204] ep_squashfs_probe: valid squashfs image detected

Real-World Use Cases

Use caseWhy squashfs fits
Consumer IoT device root filesystemImmutable base image resists tampering and field corruption; updates replace the whole partition
A/B or dual-bank OTA update schemesEach bank is a fresh squashfs image; there is no “merge” step to get wrong
Live Linux distribution ISOsHigh compression keeps the ISO small while still booting a full desktop environment
Read-only application/firmware payload on top of a writable overlayCombine with overlayfs to get an immutable base plus a writable layer for user data

Common Mistakes And Troubleshooting

  • Mounting squashfs directly on a raw MTD device. This works until the first bad block appears, then fails unpredictably. Always route through UBI’s block emulation on NAND.
  • Choosing gzip out of habit. On modern embedded CPUs, zstd usually decompresses faster at a similar or better compression ratio — benchmark both on your actual hardware.
  • Forgetting that squashfs has no write path at all. Applications that try to write configuration files directly into a squashfs-mounted tree will fail with EROFS; pair squashfs with a writable tmpfs or overlay for anything that must change at runtime.
  • Sizing the UBI volume too tightly. Leave headroom above the squashfs image size for UBI’s own wear-leveling reserve, or volume creation will fail on smaller NAND parts.

Best Practices

  • Pair squashfs with an overlay filesystem for any files that genuinely need to change at runtime, rather than weakening the read-only guarantee.
  • Version and checksum your squashfs images as part of your build pipeline so a corrupted OTA download can be detected before it is ever flashed.
  • Keep a golden factory-reset squashfs image on a separate, never-updated partition as a recovery fallback.

Performance And Security Considerations

Decompression happens per-block on read, so squashfs trades a small amount of CPU time for a large reduction in flash footprint and, indirectly, boot-time I/O — reading a compressed 20 MiB image is often faster than reading an uncompressed 60 MiB one on slow flash. On the security side, an immutable root filesystem is one of the most effective defenses against persistent malware on an embedded device: with no write path in the driver itself, there is no filesystem-level attack surface for tampering with the base image at all.

Summary And Key Takeaways

  • squashfs is a compressed, read-only filesystem ideal for immutable root filesystems and firmware payloads.
  • It achieves strong compression by dropping all write-support bookkeeping and packing small files into shared fragment blocks.
  • It is not bad-block-aware, so on raw NAND it must sit on top of a UBI volume exposed through CONFIG_MTD_UBI_BLOCK.
  • Updates work by replacing the whole image, which is exactly the property that makes squashfs a natural fit for A/B OTA update schemes.

Conclusion

squashfs is a small piece of technology with an outsized impact on embedded Linux reliability. By refusing to be writable at all, it eliminates an entire category of field failures — partial writes, power-loss corruption, and runtime tampering — while its compression keeps flash costs down. Combined with UBI on raw NAND, or used directly on managed flash, it is one of the highest-value additions you can make to a production storage strategy, and a core topic in any serious free Linux kernel development course.

FAQ

Can I write to a mounted squashfs filesystem at all?

No. The squashfs kernel driver has no write path; any write attempt returns EROFS. Combine it with overlayfs if you need a writable layer.

Why is squashfs not bad-block-aware?

It was designed assuming reliable block storage (NOR flash, managed flash, or emulated block devices), so it does not implement the bad-block mapping logic that JFFS2/UBIFS build in for raw NAND.

What compression algorithm should I use with mksquashfs today?

zstd generally offers the best balance of compression ratio and decompression speed on modern embedded CPUs, though you should benchmark gzip, lzo, xz, and zstd on your specific hardware and dataset.

How do I update a squashfs-based root filesystem in the field?

Erase the target partition (or its paired A/B bank) and program a completely new squashfs image; there is no in-place patch mechanism.

Can squashfs be used on eMMC or SD without UBI?

Yes. Managed flash already presents a reliable block device, so squashfs can be mounted directly without any UBI layer in between.

Is squashfs still actively maintained?

Yes, unlike romfs and cramfs which are considered obsolete, squashfs continues to receive active kernel maintenance and is widely used in production and in Linux live-boot media.

Continue the free Linux kernel development course

Next up: tmpfs and other temporary filesystems for volatile, RAM-backed data.

Next Lesson Course Index

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *