Skip to content

Commit 397e18e

Browse files
authored
fix ext4.Remove() missing checksum in GDT (#309)
Signed-off-by: Avi Deitcher <[email protected]>
1 parent 2ac8b67 commit 397e18e

File tree

2 files changed

+68
-17
lines changed

2 files changed

+68
-17
lines changed

filesystem/ext4/checksum.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,8 @@ func directoryChecksumAppender(seed, inodeNumber, inodeGeneration uint32) checks
4848
func nullDirectoryChecksummer(b []byte) []byte {
4949
return b
5050
}
51+
52+
// bitmapChecksum calculate the checksum for a bitmap
53+
func bitmapChecksum(b []byte, hashSeed uint32) uint32 {
54+
return crc.CRC32c(hashSeed, b)
55+
}

filesystem/ext4/ext4.go

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,13 @@ func (fs *FileSystem) Rm(p string) error {
883883
// If path is a file, it will remove the file.
884884
// Will not remove any parents.
885885
// Error if the file does not exist or is not an empty directory
886+
//
887+
//nolint:gocyclo // yes, this has high cyclomatic complexity, but we can accept it
886888
func (fs *FileSystem) Remove(p string) error {
889+
gdtBlock := 1
890+
if fs.superblock.blockSize == 1024 {
891+
gdtBlock = 2
892+
}
887893
parentDir, entry, err := fs.getEntryAndParent(p)
888894
if err != nil {
889895
return err
@@ -923,15 +929,12 @@ func (fs *FileSystem) Remove(p string) error {
923929
if err != nil {
924930
return fmt.Errorf("could not read extents for inode %d for %s: %v", entry.inode, p, err)
925931
}
926-
// clear the inode from the inode bitmap
927-
inodeBG := blockGroupForInode(int(entry.inode), fs.superblock.inodesPerGroup)
928-
inodeBitmap, err := fs.readInodeBitmap(inodeBG)
929-
if err != nil {
930-
return fmt.Errorf("could not read inode bitmap: %v", err)
931-
}
932932
// clear up the blocks from the block bitmap. We are not clearing the block content, just the bitmap.
933933
// keep a cache of bitmaps, so we do not have to read them again and again
934934
blockBitmaps := make(map[int]*bitmap.Bitmap)
935+
freedByBG := make(map[int]uint32)
936+
var totalFreed uint64
937+
935938
for _, e := range extents {
936939
for i := e.startingBlock; i < e.startingBlock+uint64(e.count); i++ {
937940
// determine what block group this block is in, and read the bitmap for that blockgroup
@@ -949,12 +952,22 @@ func (fs *FileSystem) Remove(p string) error {
949952
if err := dataBlockBitmap.Clear(blockInBG); err != nil {
950953
return fmt.Errorf("could not clear block bitmap for block %d: %v", i, err)
951954
}
955+
freedByBG[bg]++
956+
totalFreed++
952957
}
953958
}
954959
for bg, dataBlockBitmap := range blockBitmaps {
955960
if err := fs.writeBlockBitmap(dataBlockBitmap, bg); err != nil {
956961
return fmt.Errorf("could not write block bitmap back to disk: %v", err)
957962
}
963+
gd := fs.groupDescriptors.descriptors[bg]
964+
// Increment free blocks by actual filesystem blocks we just cleared in THIS group
965+
gd.freeBlocks += freedByBG[bg]
966+
gd.blockBitmapChecksum = bitmapChecksum(dataBlockBitmap.ToBytes(), fs.superblock.checksumSeed)
967+
gdBytes := gd.toBytes(fs.superblock.gdtChecksumType(), fs.superblock.checksumSeed)
968+
if _, err := writableFile.WriteAt(gdBytes, fs.start+int64(gdtBlock)*int64(fs.superblock.blockSize)+int64(gd.number)*int64(fs.superblock.groupDescriptorSize)); err != nil {
969+
return fmt.Errorf("could not write Group Descriptor bytes to file: %v", err)
970+
}
958971
}
959972

960973
// remove the directory entry from the parent
@@ -967,7 +980,10 @@ func (fs *FileSystem) Remove(p string) error {
967980
}
968981
parentDir.entries = newEntries
969982
// write the parent directory back
970-
dirBytes := parentDir.toBytes(fs.superblock.blockSize, directoryChecksumAppender(fs.superblock.checksumSeed, parentDir.inode, 0))
983+
dirBytes := parentDir.toBytes(
984+
fs.superblock.blockSize,
985+
directoryChecksumAppender(fs.superblock.checksumSeed, parentDir.inode, 0),
986+
)
971987
parentInode, err := fs.readInode(parentDir.inode)
972988
if err != nil {
973989
return fmt.Errorf("could not read inode %d for %s: %v", entry.inode, path.Base(p), err)
@@ -976,38 +992,68 @@ func (fs *FileSystem) Remove(p string) error {
976992
if err != nil {
977993
return fmt.Errorf("could not read extents for inode %d for %s: %v", entry.inode, path.Base(p), err)
978994
}
995+
// write the directory bytes back to the blocks, ensure block-aligned
996+
bs := int(fs.superblock.blockSize)
997+
written := 0
979998
for _, e := range extents {
980999
for i := 0; i < int(e.count); i++ {
981-
b := dirBytes[i:fs.superblock.blockSize]
982-
if _, err := writableFile.WriteAt(b, (int64(i)+int64(e.startingBlock))*int64(fs.superblock.blockSize)); err != nil {
1000+
if written >= len(dirBytes) {
1001+
break
1002+
}
1003+
start := written
1004+
end := start + bs
1005+
if end > len(dirBytes) {
1006+
end = len(dirBytes)
1007+
}
1008+
b := dirBytes[start:end]
1009+
1010+
fileOff := fs.start + (int64(e.startingBlock)+int64(i))*int64(bs)
1011+
1012+
if _, err := writableFile.WriteAt(b, fileOff); err != nil {
9831013
return fmt.Errorf("could not write inode bitmap back to disk: %v", err)
9841014
}
1015+
// If the last block is short, zero-pad the remainder up to block size
1016+
if len(b) < bs {
1017+
zeros := make([]byte, bs-len(b))
1018+
if _, err := writableFile.WriteAt(zeros, fileOff+int64(len(b))); err != nil {
1019+
return fmt.Errorf("could not pad directory block: %w", err)
1020+
}
1021+
}
1022+
written += bs
9851023
}
9861024
}
9871025

1026+
// clear the inode from the inode bitmap
1027+
inodeBG := blockGroupForInode(int(entry.inode), fs.superblock.inodesPerGroup)
1028+
inodeBitmap, err := fs.readInodeBitmap(inodeBG)
1029+
if err != nil {
1030+
return fmt.Errorf("could not read inode bitmap: %v", err)
1031+
}
1032+
9881033
// remove the inode from the bitmap and write the inode bitmap back
9891034
// inode is absolute, but bitmap is relative to block group
9901035
inodeInBG := int(entry.inode) - int(fs.superblock.inodesPerGroup)*inodeBG
9911036
if err := inodeBitmap.Clear(inodeInBG); err != nil {
9921037
return fmt.Errorf("could not clear inode bitmap for inode %d: %v", entry.inode, err)
9931038
}
994-
9951039
// write the inode bitmap back
9961040
if err := fs.writeInodeBitmap(inodeBitmap, inodeBG); err != nil {
9971041
return fmt.Errorf("could not write inode bitmap back to disk: %v", err)
9981042
}
999-
// update the group descriptor
1043+
1044+
// Update the group descriptor: free inode count, free block count, used directory count; recompute checksums, and write GD
10001045
gd := fs.groupDescriptors.descriptors[inodeBG]
10011046

10021047
// update the group descriptor inodes and blocks
10031048
gd.freeInodes++
10041049
gd.freeBlocks += uint32(removedInode.blocks)
1005-
// write the group descriptor back
1006-
gdBytes := gd.toBytes(fs.superblock.gdtChecksumType(), fs.superblock.uuid.ID())
1007-
gdtBlock := 1
1008-
if fs.superblock.blockSize == 1024 {
1009-
gdtBlock = 2
1050+
if entry.fileType == dirFileTypeDirectory {
1051+
gd.usedDirectories--
10101052
}
1053+
gd.inodeBitmapChecksum = bitmapChecksum(inodeBitmap.ToBytes(), fs.superblock.checksumSeed)
1054+
1055+
// write the group descriptor back
1056+
gdBytes := gd.toBytes(fs.superblock.gdtChecksumType(), fs.superblock.checksumSeed)
10111057
if _, err := writableFile.WriteAt(gdBytes, fs.start+int64(gdtBlock)*int64(fs.superblock.blockSize)+int64(gd.number)*int64(fs.superblock.groupDescriptorSize)); err != nil {
10121058
return fmt.Errorf("could not write Group Descriptor bytes to file: %v", err)
10131059
}
@@ -1594,7 +1640,7 @@ func (fs *FileSystem) allocateInode(parent uint32) (uint32, error) {
15941640
gd.freeInodes--
15951641

15961642
// get the group descriptor as bytes
1597-
gdBytes := gd.toBytes(fs.superblock.gdtChecksumType(), fs.superblock.uuid.ID())
1643+
gdBytes := gd.toBytes(fs.superblock.gdtChecksumType(), fs.superblock.checksumSeed)
15981644

15991645
// write the group descriptor bytes
16001646
// gdt starts in block 1 of any redundant copies, specifically in BG 0

0 commit comments

Comments
 (0)