The previous lecture in this free embedded Linux course covered how JFFS2 works internally. This one is the practical companion: how to actually format a partition, build a filesystem image from a staging directory, program it into flash, and get a board to boot from it.
What You Will Learn
Prerequisites
Before this lesson
Two Ways to Get a JFFS2 Filesystem
There are really only two paths to a working JFFS2 filesystem, and this free embedded Linux course walks through both: format an already-flashed, empty partition at runtime, or build a complete image offline and program it into flash before first boot. Production devices almost always use the second path, since manufacturing needs a reproducible image, not a runtime formatting step.
Method 1: Formatting an Empty Partition at Runtime
A blank JFFS2 filesystem is nothing more than a fully erased set of blocks with clean markers written — there is no separate “format” step the way there is for ext4’s mkfs. On a running system with an MTD partition already mapped, formatting partition 6 looks like this:
\# flash_erase -j /dev/mtd6 0 0
\# mount -t jffs2 mtd6 /mnt
The -j flag tells flash_erase to write clean markers as it erases, which is what lets JFFS2 mount the result as an empty, valid filesystem instead of complaining about corrupt data. Note the device name given to mount is mtd6, not /dev/mtd6 — a long-standing JFFS2 quirk. The raw block device node /dev/mtdblock6 works too. Once mounted, treat it like any other filesystem: files written to it survive a reboot and remount.
Method 2: Building an Offline Image
For a reproducible manufacturing image, build the JFFS2 filesystem directly from a staging rootfs directory with mkfs.jffs2, then optionally run sumtool over the result to add summary nodes for faster mount times, as covered in the previous lecture. Both tools ship in the mtd-utils package.
$ mkfs.jffs2 -n -e 0x20000 -p -d ~/rootfs -o ~/rootfs.jffs2
$ sumtool -n -e 0x20000 -p -i ~/rootfs.jffs2 -o ~/rootfs-sum.jffs2
| Flag | Meaning |
|---|---|
-e 0x20000 | Erase block size of the target flash — here 128 KB. Must match the real hardware. |
-p | Pad the output image to a whole number of erase blocks. |
-n | Suppress clean markers in the image. Use this for NAND, where the clean marker lives in the out-of-band area instead of the main data area. Leave it out for NOR. |
-d ~/rootfs | Source directory whose contents become the filesystem image. |
-D [table] | Optional device table to set file permissions and ownership without needing root privileges on the build machine. |
Buildroot and the Yocto Project both wrap this exact sequence internally, so in day-to-day work you will often just enable a JFFS2 image target in your build system’s configuration rather than invoking these tools by hand — but knowing what happens underneath makes debugging image-size or mount problems far easier.
Programming the Image Into Flash
From U-Boot, with the image already loaded into RAM at address 0x82000000 and a target flash region starting at offset 0x163000 spanning 0x7a9d000 bytes:
nand erase clean 163000 7a9d000
nand write 82000000 163000 7a9d000
The equivalent operation from a running Linux system, writing the same summary-enabled image over the mtd-utils tools:
\# flash_erase -j /dev/mtd6 0 0
\# nandwrite /dev/mtd6 rootfs-sum.jffs2
Booting From a JFFS2 Root Filesystem
JFFS2 cannot be auto-detected at mount time the way some filesystems can, so the kernel needs to be told explicitly both which block device holds the root filesystem and what type it is:
root=/dev/mtdblock6 rootfstype=jffs2
Leaving out rootfstype=jffs2 is one of the most common reasons a board that flashes successfully still fails to boot — the kernel falls through its filesystem probes and panics looking for a root filesystem it cannot identify.
Build and Run: An End-to-End Verification Script
Here is an original helper script — not lifted from any book — that builds a small staging rootfs, creates a JFFS2 image, adds summary nodes, and verifies the result, all on a development machine with mtd-utils installed. It uses a loopback-mounted MTD RAM device where available so you can sanity-check the pipeline without touching real hardware.
#!/bin/sh
# ep_jffs2_build.sh - build and sanity-check a JFFS2 image end to end
# Usage: ./ep_jffs2_build.sh
set -e
STAGE="${1:-./ep_stage}"
ERASESIZE="${2:-0x20000}"
OUT="${3:-ep_rootfs}"
mkdir -p "$STAGE"
echo "hello from ep_jffs2_build" > "$STAGE/hello.txt"
mkdir -p "$STAGE/bin"
echo "[1/3] Building base image with mkfs.jffs2 ..."
mkfs.jffs2 -n -e "$ERASESIZE" -p -d "$STAGE" -o "${OUT}.jffs2"
echo "[2/3] Adding summary nodes with sumtool ..."
sumtool -n -e "$ERASESIZE" -p -i "${OUT}.jffs2" -o "${OUT}-sum.jffs2"
echo "[3/3] Image sizes:"
ls -lh "${OUT}.jffs2" "${OUT}-sum.jffs2"
echo "Done. Program ${OUT}-sum.jffs2 with nandwrite or your bootloader's flash tools."
Expected output on a typical development machine:
$ chmod +x ep_jffs2_build.sh
$ ./ep_jffs2_build.sh
[1/3] Building base image with mkfs.jffs2 ...
[2/3] Adding summary nodes with sumtool ...
[3/3] Image sizes:
-rw-r--r-- 1 dev dev 1.2M ep_rootfs.jffs2
-rw-r--r-- 1 dev dev 1.3M ep_rootfs-sum.jffs2
Done. Program ep_rootfs-sum.jffs2 with nandwrite or your bootloader's flash tools.
The slightly larger summary-enabled image is expected — that is the roughly 5% storage overhead discussed in the previous lecture, traded for a two-to-five times faster mount.
Common Mistakes and Troubleshooting
Watch out for these
Best Practices, Performance, and Security
Performance: always generate summary nodes for production NAND images; the mount-time savings compound across every boot cycle in the field.
Security: a flashed JFFS2 image is fully readable by anyone with physical or bootloader access to the chip; treat any credentials baked into a staging rootfs the same way you would treat them on an unencrypted disk image.
Summary and Key Takeaways
With image creation and booting covered, the next lecture in this free embedded Linux course moves on to YAFFS2 — a NAND-focused alternative to JFFS2 with a different set of trade-offs.
Frequently Asked Questions
Do I need sumtool if I’m just testing on a development board?
Not strictly — mkfs.jffs2’s output alone mounts fine. Summary nodes matter most once partitions get large enough that mount time becomes noticeable, which is more of a production concern.
Why does mkfs.jffs2 need the erase block size as an argument?
JFFS2 packs data into whole erase blocks internally. If the image doesn’t align with the real hardware’s block size, the filesystem structure on flash won’t match what the driver expects.
Can I resize a JFFS2 partition after it has data on it?
Not in place. You would mount the old image, copy its contents into a new staging directory, and rebuild the image at the new size with mkfs.jffs2.
What’s the difference between nandwrite and a plain dd to the MTD device?
nandwrite is bad-block aware — it skips factory-marked bad blocks on NAND automatically. A plain dd to /dev/mtdX has no such awareness and can silently corrupt data written across a bad block.
Does Buildroot or Yocto still need manual mkfs.jffs2 invocation?
No — both build systems wrap this pipeline internally once you select a JFFS2 image type in their configuration; manual invocation is mainly for debugging or one-off image builds.
Keep Learning Embedded Linux for Free
Continue this free embedded Linux course with the next lecture on the YAFFS2 filesystem.
Next Lecture Browse Full Course
2 Comments