Building UBIFS Filesystem Images-Best Embedded Linux Training Online

PREV_LEC | NEXT_LEC

Building UBIFS Filesystem Images

A free embedded Linux course lecture on UBIFS, the flash filesystem built on top of UBI volumes

16 min read
Reading time
Intermediate
Level
Chapter 7
Storage Strategy

In the previous lecture of this free linux kernel development course we built and inspected UBI volumes — the wear-leveled logical storage layer that sits on top of raw NAND. UBI on its own is not a filesystem; it just hands you block devices. This lecture covers UBIFS, the filesystem designed specifically to run on top of a UBI volume, and walks through the two-stage process of turning a directory tree into a bootable UBIFS image.

Keywords covered in this lecture

UBIFS mkfs.ubifs ubinize write-back caching UBIFS journal fsync / fdatasync free embedded linux course free linux device drivers course

What You Will Learn

Why UBIFS needs UBI underneath it How UBIFS mounting differs from JFFS2/YAFFS2 What the UBIFS journal is for Building a UBIFS image with mkfs.ubifs Embedding that image into a UBI volume with ubinize Mounting UBIFS volumes at runtime Using fsync/fdatasync safely with write-back caching

Prerequisites

This lecture assumes you have already completed the previous lecture on the UBI flash volume manager in this free linux kernel development course, and are comfortable with:

Creating and attaching UBI devices with ubiformat/ubiattach Creating UBI volumes with ubimkvol Basic filesystem mounting concepts on Linux

Why UBIFS Sits on UBI, Not on MTD Directly

JFFS2 and YAFFS2, covered earlier in this chapter, both talk directly to an MTD device and implement their own wear leveling internally. UBIFS takes a different approach: it assumes wear leveling and bad-block handling are already solved by the UBI layer underneath it, and instead focuses entirely on being a fast, robust filesystem on top of that. This division of labor — UBI handles the flash, UBIFS handles the files — is what lets UBIFS avoid duplicating logic that JFFS2 and YAFFS2 each implement separately, and it is why UBIFS has become the default choice for new raw-NAND embedded Linux products.

The practical consequence is that UBIFS is always mounted against a UBI volume device node — never directly against an MTD device. If you skip the UBI attach/volume-creation steps from the previous lecture, there is nothing for UBIFS to mount.

What Makes UBIFS Different: Index-on-Flash and the Journal

Unlike JFFS2 and YAFFS2, which rebuild their entire index in RAM by scanning the flash at mount time, UBIFS stores its index information on the flash itself. That means mounting is fast even on very large volumes, because UBIFS doesn’t need to replay the whole filesystem to reconstruct where every file lives — it just reads the on-flash index directly. The trade-off is that attaching the underlying UBI volume beforehand can still take noticeable time on large NAND chips (addressed by UBI fastmap, covered in the previous lecture), so the overall “time to first mount” depends on both layers.

UBIFS also supports write-back caching, much like a conventional disk filesystem: writes land in memory first and are flushed to flash later, which makes write-heavy workloads dramatically faster than JFFS2’s write-through behaviour. The cost is the usual write-back tradeoff — data that hasn’t been flushed yet is lost on a power failure. UBIFS mitigates this with a journal, a reserved area of flash (typically 4 MiB or more) used to recover the filesystem quickly after an unclean power-down, replaying only the recent journal entries instead of scanning the whole volume. Because the journal consumes a meaningful, fixed chunk of space, UBIFS is not a good fit for very small flash devices — on a 4 MB partition, the journal alone could be a large fraction of total capacity.

UBIFS Layered on Top of UBI and MTD

+————————————————————-+ | APPLICATIONS | | (open/read/write/fsync calls) | +————————————————————-+ | v +————————————————————-+ | UBIFS | | on-flash index | write-back cache | journal | +————————————————————-+ | mounts on top of v +————————————————————-+ | UBI | | PEB LEB mapping, wear leveling, bad PEBs | +————————————————————-+ | operates through v +————————————————————-+ | MTD | | raw NAND flash chip | +————————————————————-+

Stage 1: Building the UBIFS Image with mkfs.ubifs

Creating a UBIFS-backed rootfs is a two-stage process. The first stage packs an ordinary directory tree into a UBIFS image file using mkfs.ubifs. Three parameters matter most:

FlagMeaning
-rSource directory to pack into the image
-mFlash page size (minimum I/O unit)
-eLEB size — remember this is one page smaller than the PEB, as covered in the UBI lecture
-cMaximum number of LEBs the resulting volume may grow to
-oOutput image filename

For a target volume where the physical erase block is 128 KiB and the page size is 2048 bytes, the LEB size works out to 126 KiB (128 KiB minus one 2 KiB page), and a 32 MiB volume needs 256 such LEBs:

# Build a UBIFS image from the ./rootfs directory
mkfs.ubifs -r rootfs -m 2048 -e 126KiB -c 256 -o rootfs.ubi

Stage 2: Embedding the Image with ubinize

The output of mkfs.ubifs is a raw UBIFS image — it is not yet something you can flash directly, because it still needs to be embedded into a proper UBI volume structure (with the UBI headers UBI itself expects). That is the job of ubinize, which is driven by a small configuration file describing each volume to create. Here is an original example configuration, ep-ubinize.cfg, describing a single rootfs volume:

; ep-ubinize.cfg - describes one UBI volume for ubinize
[ep_rootfs_volume]
mode=ubi
image=rootfs.ubi
vol_id=0
vol_name=rootfs
vol_type=dynamic
vol_alignment=1
vol_flags=autoresize

Run ubinize against this configuration to produce a flashable UBI image:

# Embed rootfs.ubi into a proper UBI image ready to flash
ubinize -o ubi-image.img -m 2048 -p 128KiB -s 2048 ep-ubinize.cfg

The resulting ubi-image.img is what you actually write to the MTD partition (with ubiformat‘s image-loading mode, or via your bootloader’s flashing tool), rather than the intermediate rootfs.ubi file produced in stage one.

Mounting a UBIFS Volume

Once the UBI volumes exist — whether created fresh with ubimkvol and then formatted with UBIFS at runtime, or pre-built with mkfs.ubifs/ubinize and flashed at manufacturing time — mounting works like any other filesystem, using either the per-volume device node or the device-plus-name form:

# Mount using the UBI volume device node
mount -t ubifs /dev/ubi0_0 /mnt

# Equivalent: mount using device name + volume name
mount -t ubifs ubi0:rootfs /mnt

Using fsync and fdatasync Safely

Because UBIFS caches writes in memory before flushing them to flash, an application that needs a guarantee that specific data has actually survived a power loss must explicitly force that flush. Here is a small original example, ep_ubifs_sync_demo.c, that demonstrates writing a critical record and forcing it to flash before continuing:

/* ep_ubifs_sync_demo.c - force critical data to flash on UBIFS */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    if (argc != 2) {
        fprintf(stderr, "usage: %s /mnt/config.dat\n", argv[0]);
        return 1;
    }

    const char *record = "ep_config_version=7\n";

    int fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    if (write(fd, record, strlen(record)) < 0) {
        perror("write");
        close(fd);
        return 1;
    }

    /* Force the write-back cache to flush this file's data to flash
     * before we report success -- without this, a power loss right
     * after write() could lose the record even though write() itself
     * returned success. */
    if (fdatasync(fd) < 0) {
        perror("fdatasync");
        close(fd);
        return 1;
    }

    printf("ep_ubifs_sync_demo: record committed to flash\n");
    close(fd);
    return 0;
}
$ gcc -o ep_ubifs_sync_demo ep_ubifs_sync_demo.c
$ ./ep_ubifs_sync_demo /mnt/config.dat
ep_ubifs_sync_demo: record committed to flash

fdatasync(2) flushes file data (and only the metadata needed to retrieve it) without necessarily flushing every metadata attribute, which is usually enough and cheaper than a full fsync(2). Reserve fsync for cases where metadata like timestamps or file size also has to be crash-consistent.

JFFS2 vs YAFFS2 vs UBIFS at a Glance

FeatureJFFS2YAFFS2UBIFS
Sits on top ofMTD directlyMTD directlyUBI volume
Wear levelingBuilt-in, per-filesystemBuilt-in, per-filesystemDelegated to UBI, shared across volumes
Mount-time index rebuildFull scanFull scanIndex stored on flash — fast
Write cachingWrite-throughWrite-throughWrite-back with journal
Good fit for very small flashYesYesNo — journal overhead

Real-World Use Cases

Root filesystems on modern raw-NAND embedded Linux boards Write-heavy data partitions (logging, telemetry buffers) that benefit from write-back caching Products needing fast mount times on large flash devices Systems where crash-consistency for critical files is required via fsync/fdatasync

Common Mistakes and Troubleshooting

SymptomLikely CauseFix
mkfs.ubifs fails with a size mismatch-e value doesn’t account for the one-page LEB overheadRecompute LEB size as PEB size minus one page, not the raw PEB size
Mount fails with “no UBI device” errorsTrying to mount UBIFS directly against an MTD deviceAttach and create a UBI volume first; UBIFS never mounts on raw MTD
Data lost after an unexpected power cutRelying on write() alone without fsync/fdatasync for critical dataCall fdatasync (or fsync where metadata matters) at crucial commit points
Filesystem fails to fit on a small flash chipJournal overhead (4 MiB+) consuming too much of a small partitionUse JFFS2 instead on very small flash devices

Best Practices

Always build UBI volumes before attempting to build or mount UBIFS images Use fdatasync for performance-sensitive commits, fsync only when metadata must also be durable Budget journal overhead explicitly when sizing small flash partitions Keep mkfs.ubifs LEB-size parameters consistent with the UBI volume’s actual LEB size

Performance consideration: write-back caching is UBIFS’s biggest performance win over JFFS2, but only for workloads that don’t need every write durable immediately — force syncs sparingly, only at genuine commit points. Security consideration: like UBI, UBIFS provides no encryption on its own; use dm-crypt or a similar layer beneath UBI if confidentiality is required.

Summary and Key Takeaways

UBIFS always mounts on a UBI volume, never directly on MTD On-flash indexing makes UBIFS mount fast even on large volumes Write-back caching plus a journal trades some power-loss risk for much better write performance Building an image is a two-stage process: mkfs.ubifs then ubinize fsync/fdatasync are how applications opt back into durability where it matters

Conclusion

UBIFS is the natural conclusion of the UBI story from the previous lecture: once wear leveling and bad-block handling are solved once, generically, UBIFS is free to focus purely on being a fast, journaled filesystem with on-flash indexing and write-back caching. Together, UBI and UBIFS form the storage stack behind the majority of modern raw-NAND embedded Linux products, and completing both lectures gives you everything needed to design, build, and troubleshoot that stack on real hardware — rounding out the storage strategy chapter of this free linux kernel development course.

Frequently Asked Questions

Can UBIFS be mounted directly on an MTD device?

No. UBIFS is designed to run on top of a UBI volume, which itself sits on top of MTD. There is no supported path to mount UBIFS directly against a raw MTD device.

Why does UBIFS mount faster than JFFS2 on large flash?

UBIFS stores its index information on the flash itself, so mounting reads that index directly instead of scanning the entire volume, unlike JFFS2 and YAFFS2 which rebuild their index by scanning at mount time.

What is the UBIFS journal for?

The journal is a reserved area of flash used for fast recovery after an unclean power-down. Instead of scanning the whole filesystem, UBIFS can replay just the recent journal entries to restore a consistent state.

Is UBIFS suitable for very small flash chips?

Generally no. The journal typically consumes 4 MiB or more, which can be a large fraction of a very small device’s total capacity — JFFS2 is usually a better fit there.

What is the difference between fsync and fdatasync?

fsync flushes both file data and all metadata to flash. fdatasync flushes the data and only the metadata strictly needed to retrieve it (like file size), which is often sufficient and cheaper.

Why does building a UBIFS image require two separate tools?

mkfs.ubifs packs a directory tree into a raw UBIFS image, but that image still needs to be wrapped with the UBI headers a UBI volume expects — that wrapping step is what ubinize does, producing a final image ready to flash.

Does write-back caching in UBIFS risk data loss?

Yes, for data that hasn’t been flushed yet at the moment of an unexpected power loss. Applications that need specific writes to be crash-durable should call fsync or fdatasync at the appropriate commit points.

Continue this free embedded Linux course

You’ve now covered the full raw-flash storage stack: MTD, JFFS2, YAFFS2, UBI, and UBIFS.

Explore More Free Lectures Back to Course Index

PREV_LEC | NEXT_LEC

2 Comments

Leave a Reply

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