Skip to content

Add native Go ext4 filesystem image creator#280

Merged
dmcgowan merged 1 commit intocontainerd:mainfrom
dmcgowan:add-ext4-library
Apr 14, 2026
Merged

Add native Go ext4 filesystem image creator#280
dmcgowan merged 1 commit intocontainerd:mainfrom
dmcgowan:add-ext4-library

Conversation

@dmcgowan
Copy link
Copy Markdown
Member

Pure Go implementation of ext4 formatting that produces valid, optimized sparse images without requiring mkfs.ext4 or any host binaries. Output is byte-comparable to mkfs.ext4.

This is designed to be used with the erofs snapshotter to remove the dependency on a host binary and speed up upper creation to speed up overall container startup time.

Why not an existing implementation?

The only Go library that supports ext4 creation is go-diskfs. Other Go ext4 packages (dsoprea/go-ext4, masahiro331/go-ext4-filesystem) are read-only. Microsoft's hcsshim/ext4/tar2ext4 converts tarballs to ext4 but does not support creating empty formatted images.

go-diskfs is a general-purpose disk and filesystem library (~9,000 lines for ext4 alone, plus FAT12/16/32, ISO 9660, squashfs, and GPT/MBR partitioning). It seems to be designed for writing to block devices, not creating sparse file images. Several aspects make it impractical for our use case:

Eager zero-fill architecture. go-diskfs explicitly writes zeros to every inode table for every block group (ext4.go:3069), every bitmap (ext4.go:3045-3057), and the entire journal in 1 MiB chunks (ext4.go:2800-2816). This destroys file sparseness — a 256 MiB image consumes 12 MiB on disk vs 4 MiB with sparse writes. Changing this requires rewriting the creation path across initGroupDescriptorTables, buildGroupDescriptorsFromSuperblock, initJournal, and the Create orchestration (~300 lines spanning multiple functions). The layout calculations could survive, but the write path would be entirely new.

Fails e2fsck at 256 MiB and above. Empty block groups are never marked with INODE_UNINIT or BLOCK_UNINIT flags (ext4.go:3367). With default settings, creation crashes at 1 GiB with a journal extent tree error. This indicates the library has not been heavily exercised at sizes relevant to container images.

18 transitive dependencies. Four compression libraries (lz4, xz, lzo, zstd), logrus, xattr handling, and others — none used by the ext4 code path.

Benchmarks (256 MiB image)

Native Format mkfs.ext4 (optimized) mkfs.ext4 (defaults) go-diskfs
Time 1.3 ms 5.0 ms 9.5 ms 26 ms
Disk usage 4 MiB 6 MiB 12 MiB 12 MiB
e2fsck pass pass pass fail
Host binaries none mkfs.ext4 mkfs.ext4 none
Dependencies stdlib only - - 18 modules

The native formatter performs the same work as mkfs.ext4 — the speed difference vs mkfs.ext4 (optimized) is primarily fork/exec and process startup overhead, not the formatting itself. The mkfs.ext4 C implementation does the same sparse-aware writes under the same optimized flags. The native formatter avoids this process overhead entirely and requires no host binaries, while producing byte-comparable output (identical superblock fields, block layout, and bitmap content — differing only in UUID and derived checksums). go-diskfs is 20x slower due to its eager zero-fill write path.

Features

  • Pure Go, no external binaries required
  • Sparse file output — only non-zero metadata blocks written to disk
  • CRC32C metadata checksums (superblock, GDT, bitmaps, inodes, directory blocks)
  • Multi-group support with flex_bg metadata consolidation
  • INODE_UNINIT / BLOCK_UNINIT flags on empty groups
  • Optional directory creation with permissions and ownership
  • Parent directories created automatically, inheriting the child's permissions

Copy link
Copy Markdown
Member

@hsiangkao hsiangkao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this idea

Comment thread pkg/ext4/format.go Outdated
Comment thread pkg/ext4/format.go
@dmcgowan dmcgowan force-pushed the add-ext4-library branch 4 times, most recently from 4e367e7 to f1cca45 Compare April 12, 2026 07:15
Pure Go implementation of ext4 formatting that produces valid,
optimized sparse images without requiring mkfs.ext4 or any host
binaries. Output is byte-comparable to mkfs.ext4.

Signed-off-by: Derek McGowan <derek@mcg.dev>
@dmcgowan dmcgowan merged commit 49d1748 into containerd:main Apr 14, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants