Filesystem Images Using Device Tables
Build device nodes into an image as a non-root user in this free linux device drivers course
Creating device nodes normally requires the mknod system call, which needs root privileges — a problem the moment your build pipeline runs as an unprivileged CI user, which is standard practice today. This lecture in our free linux kernel development course covers device tables: a plain-text format understood by several Linux filesystem image builders that lets you describe device nodes, ownership, and permissions declaratively, so the tool creates them inside the image without ever needing root on the build host.
What You Will Learn
Prerequisites
This lecture assumes you have a staged rootfs directory from earlier in this free embedded systems course, and are comfortable with basic device node concepts (major/minor numbers, node types) from the device-node lecture earlier in this chapter.
The Root Problem: mknod Needs root
If you try to bake device nodes directly into a filesystem image as an ordinary CI user, mknod fails outright — creating a character or block special file is a privileged operation by design, since it lets a process grant itself arbitrary access to physical devices or kernel interfaces. Historically this forced build systems to either run parts of the build as root inside a container, or use fakeroot-style wrapping. Device tables sidestep the problem entirely: instead of creating nodes on the host filesystem at all, you describe them in a text file, and a purpose-built tool bakes the corresponding entries directly into the output image format, with no privileged syscall required on the build host.
The Device Table Format
A device table is a plain text file, one line per entry, with this field layout:
<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
| Field | Meaning |
|---|---|
| name | Path of the entry relative to the image root |
| type | f (file), d (directory), c (char device), b (block device), p (FIFO) |
| mode | Octal permission bits |
| uid / gid | Numeric owner and group to assign in the image |
| major / minor | Device major/minor number (character or block types only) |
| start / inc / count | Optional fields for generating a numbered range of nodes, e.g. tty0..tty7 |
Fields that do not apply to a given type (major/minor for a plain file, for instance) are simply written as -.
A Worked Example
Here is a small, original device table covering a handful of common nodes plus a numbered range for serial ports:
# ep_device_table - example device table for a minimal rootfs
# name type mode uid gid major minor start inc count
/dev d 755 0 0 - - - - -
/dev/console c 600 0 0 5 1 - - -
/dev/null c 666 0 0 1 3 - - -
/dev/zero c 666 0 0 1 5 - - -
/dev/ttyS c 660 0 0 4 64 0 1 4
The last line generates four nodes in one entry — ttyS0 through ttyS3 — with minor numbers starting at 64 and incrementing by 1 for each, which is far less error-prone than writing four separate lines by hand.
Building an initramfs with gen_init_cpio
The Linux kernel source tree ships a small standalone tool, gen_init_cpio, specifically for turning a device table (plus a file list) into a cpio archive suitable for use as an initramfs. It lives under usr/ in the kernel source and can be built independently of a full kernel build:
$ gcc -O2 -o gen_init_cpio linux/usr/gen_init_cpio.c
$ ./gen_init_cpio ep_device_table.txt > rootfs.cpio
gen_init_cpio also accepts file and dir lines in the same table so you can describe your entire image — regular files pulled from the staging tree, directories, and device nodes — in one declarative document, all without touching a privileged syscall:
dir /bin 755 0 0
file /bin/busybox staging/bin/busybox 755 0 0
dir /dev 755 0 0
nod /dev/console 600 0 0 c 5 1
The Same Pattern for Other Filesystem Formats
gen_init_cpio is not unique — the same device-table concept has been adopted by the image builders for the other filesystem formats commonly used on embedded flash storage and disk:
| Filesystem | Tool | Typical Use |
|---|---|---|
| cpio (initramfs) | gen_init_cpio | Embedded into the kernel image or used standalone |
| jffs2 | mkfs.jffs2 | Raw NOR/NAND flash, no separate flash translation layer |
| ubifs | mkfs.ubifs (with ubinize) | NAND flash on top of UBI |
| ext2 | genext2fs | Disk images, eMMC, SD card storage |
Each of these tools accepts a device table via a -D flag (the exact flag name varies slightly by tool) and applies the same field layout described above. We cover jffs2 and ubifs in depth later in this course when we look at flash storage strategies; genext2fs follows the identical device table syntax for building disk-based images.
Common Mistakes and Troubleshooting
- Mismatched major/minor numbers — a device table entry with the wrong major/minor creates a node that looks correct with
ls -lbut talks to the wrong driver, or none at all. - Forgetting the type column entirely — a missing or malformed type field causes most tools to reject the whole table rather than skip one bad line.
- Using device tables for devices that should be dynamic — baking hotplug-capable devices (USB, SD card readers) into a static table defeats the point of devtmpfs/mdev covered earlier in this chapter; reserve device tables for nodes that genuinely never change.
- Path mismatches between the table and the staged files list — when combining file and device entries in one table, a typo in a source path fails the whole build, not just that one line.
Best Practices
Keep device tables under version control next to the rest of your rootfs build scripts, and comment each line with the device it corresponds to — the field-only format is not self-explanatory months later. Prefer device tables only for genuinely static, always-present nodes (console, null, zero); leave hotplug-capable hardware to devtmpfs and mdev from the earlier lecture in this chapter.
Summary and Key Takeaways
Device tables let you describe device nodes declaratively in plain text and have a build tool create them inside a filesystem image, entirely avoiding the need for root privileges on the build host. The same field format is shared across gen_init_cpio, mkfs.jffs2, mkfs.ubifs, and genext2fs, making it a single skill that applies across nearly every embedded filesystem format.
Conclusion
Device tables are a small, easily overlooked tool, but they solve a real and common build-pipeline problem cleanly. Once you understand the field layout, the same technique carries across every major embedded filesystem format covered in this free linux kernel development course, from a simple initramfs to a production jffs2 or ext2 image.
Frequently Asked Questions
Why can’t I just run mknod as part of my build script?
mknod requires root privileges, which most modern CI/build pipelines deliberately avoid granting. Device tables let the image-building tool create the nodes internally instead.
Is the device table format the same across gen_init_cpio, mkfs.jffs2, and genext2fs?
The core field layout is shared, though the exact command-line flag to pass the table file varies slightly between tools — check each tool’s help output.
Can a device table create regular files and directories, not just device nodes?
Yes, most of these tools accept file and dir entries alongside device node entries in the same table, letting you describe an entire image in one document.
What do the start, inc, and count fields do?
They generate a numbered range of nodes from a single line — for example four serial ports ttyS0 through ttyS3 with sequential minor numbers.
Should hotplug devices like USB drives go in a device table?
No, device tables are for static, always-present nodes. Hotplug-capable devices should be left to devtmpfs and mdev, covered earlier in this chapter.
Where does gen_init_cpio come from?
It ships as source inside the Linux kernel tree under usr/gen_init_cpio.c and can be compiled as a small standalone host tool.
Continue the Free Linux Kernel Development Course
Explore the rest of this chapter on building a root filesystem, or move ahead to flash storage strategies with jffs2 and ubifs.
Next Lecture Browse the Full Course
2 Comments