Skip to content

fix: set padding bits at end of inode bitmap in compactext4#2811

Open
lox wants to merge 1 commit into
microsoft:mainfrom
lox:fix-inode-bitmap-padding
Open

fix: set padding bits at end of inode bitmap in compactext4#2811
lox wants to merge 1 commit into
microsoft:mainfrom
lox:fix-inode-bitmap-padding

Conversation

@lox

@lox lox commented Jul 8, 2026

Copy link
Copy Markdown

Description

compactext4 leaves the tail bits of each group's inode bitmap clear. ext4
convention (enforced by e2fsck) is that bitmap bits beyond the last real inode
in the block are set to 1 so allocators never treat nonexistent inodes as
free. As a result, e2fsck 1.47.x reports on any tar2ext4 output:

Padding at end of inode bitmap is not set. Fix? no

and exits 4 ("filesystem still has errors"). The image is otherwise clean.

Since bestGroupCount commonly picks an inodesPerGroup well below
maxInodesPerGroup (32768), the unset padding affects every group, not just
the last. The block bitmap already handles its analogous case (blocks absent
from the disk in the last group are marked allocated); this adds the
inode-bitmap equivalent.

Padding bits are deliberately not counted in usedInodeCount: free-inode
accounting in the group descriptors and superblock is based on
inodesPerGroup real inodes. e2fsck reports identical file counts before and
after this change.

Found by running e2fsck -f -n against tar2ext4 output while benchmarking it
against another ext4 writer implementation (e2fsck 1.47.3). Existing tests
didn't catch this because the fsck() verification helper is Linux-only and
the per-block inode-bitmap padding check only appears in recent e2fsprogs.

Repro

go build ./cmd/tar2ext4
./tar2ext4 -i rootfs.tar -o rootfs.ext4
e2fsck -f -n rootfs.ext4   # exits 4 before this change, 0 after

Changes

  • Set inode bitmap bits from inodesPerGroup to BlockSize*8 in every group
    in Writer.Close()
  • Add TestInodeBitmapPadding, which parses the superblock and group
    descriptors from the emitted image and asserts the padding bits directly
    (platform-independent, doesn't depend on the host's e2fsck version), then
    runs the existing fsck() helper

Compatibility note

This changes the emitted image bytes. Images remain deterministic (the
existing tar2ext4 determinism test passes), but dm-verity root hashes
computed over newly generated images will differ from those of images
generated before this change.

ext4 convention (enforced by e2fsck) is that inode bitmap bits beyond
the last real inode in the block are set to 1 so allocators never treat
nonexistent inodes as free. compactext4 left these tail bits clear in
every group, so e2fsck 1.47.x reports "Padding at end of inode bitmap
is not set" and exits 4 on tar2ext4 output.

The block bitmap already handles its analogous case (absent blocks in
the last group are marked allocated); this adds the inode-bitmap
equivalent. Padding bits are not counted in usedInodeCount since
free-inode accounting is based on inodesPerGroup real inodes.

Found by running e2fsck -f -n against tar2ext4 output with e2fsck
1.47.3.

Signed-off-by: Lachlan Donald <lachlan@ljd.cc>
@lox lox requested a review from a team as a code owner July 8, 2026 04:30
@lox

lox commented Jul 8, 2026

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes compactext4 output to follow ext4 inode-bitmap conventions by marking inode-bitmap padding bits (beyond inodesPerGroup) as allocated, preventing e2fsck (1.47.x+) from reporting “Padding at end of inode bitmap is not set” and exiting with errors.

Changes:

  • Set inode-bitmap padding bits (inodesPerGroup .. BlockSize*8) to 1 for every group during Writer.Close().
  • Add TestInodeBitmapPadding to parse the emitted image and assert the padding bits are set (then run existing fsck() helper).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
ext4/internal/compactext4/compact.go Sets inode-bitmap tail/padding bits so nonexistent inodes are never treated as free.
ext4/internal/compactext4/compact_test.go Adds a platform-independent test that directly validates inode-bitmap padding bits in the output image.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1279 to +1285
// Bits beyond inodesPerGroup don't correspond to real inodes; e2fsck
// expects this padding to be set so allocators never consider them
// free. Don't count them in usedInodeCount: free-inode accounting is
// based on inodesPerGroup real inodes.
for j := inodesPerGroup; j < BlockSize*8; j++ {
b[BlockSize+j/8] |= 1 << (j % 8)
}
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.

2 participants