Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions ext4/internal/compactext4/compact.go
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,13 @@ func (w *Writer) Close() error {
dirCount++
}
}
// 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)
}
Comment on lines +1279 to +1285
_, err := w.write(b[:])
if err != nil {
return err
Expand Down
67 changes: 67 additions & 0 deletions ext4/internal/compactext4/compact_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,73 @@ func TestBasic(t *testing.T) {
runTestsOnFiles(t, testFiles)
}

// TestInodeBitmapPadding verifies that inode bitmap bits beyond
// inodesPerGroup are set in every group. e2fsck (1.47.x and later) reports
// "Padding at end of inode bitmap is not set" otherwise, and allocators
// could treat nonexistent inodes as free.
func TestInodeBitmapPadding(t *testing.T) {
image := "testfs.img"
imagef, err := os.Create(image)
if err != nil {
t.Fatal(err)
}
defer os.Remove(image)
defer imagef.Close()

w := NewWriter(imagef)
testFiles := []testFile{
{Path: "dir", File: &File{Mode: format.S_IFDIR | 0755}},
{Path: "dir/file", File: &File{Mode: 0644}, Data: data[:40]},
}
for _, tf := range testFiles {
createTestFile(t, w, tf)
}
if err := w.Close(); err != nil {
t.Fatal(err)
}

var sb format.SuperBlock
if _, err := imagef.Seek(1024, io.SeekStart); err != nil {
t.Fatal(err)
}
if err := binary.Read(imagef, binary.LittleEndian, &sb); err != nil {
t.Fatal(err)
}
if sb.Magic != format.SuperBlockMagic {
t.Fatalf("bad superblock magic %#x", sb.Magic)
}
if sb.InodesPerGroup >= BlockSize*8 {
t.Fatalf("test image has no inode bitmap padding (inodesPerGroup=%d)", sb.InodesPerGroup)
}
groups := sb.InodesCount / sb.InodesPerGroup

gds := make([]format.GroupDescriptor, groups)
if _, err := imagef.Seek(BlockSize, io.SeekStart); err != nil {
t.Fatal(err)
}
if err := binary.Read(imagef, binary.LittleEndian, gds); err != nil {
t.Fatal(err)
}

var bitmap [BlockSize]byte
for g, gd := range gds {
if _, err := imagef.ReadAt(bitmap[:], int64(gd.InodeBitmapLow)*BlockSize); err != nil {
t.Fatal(err)
}
for j := sb.InodesPerGroup; j < BlockSize*8; j++ {
if bitmap[j/8]&(1<<(j%8)) == 0 {
t.Fatalf("group %d: inode bitmap padding bit %d is not set", g, j)
}
}
// Padding bits must not be counted as used inodes.
if uint32(gd.FreeInodesCountLow) > sb.InodesPerGroup {
t.Errorf("group %d: free inode count %d exceeds inodesPerGroup %d", g, gd.FreeInodesCountLow, sb.InodesPerGroup)
}
}

fsck(t, image)
}

func TestLargeDirectory(t *testing.T) {
testFiles := []testFile{
{Path: "bigdir", File: &File{Mode: format.S_IFDIR | 0755}},
Expand Down