Skip to content

# every MinixFs volume we wrote failed fsck.minix, and the zone map was counted wrong too #77

Description

@Hawkynt

Found by handing a volume we wrote to fsck.minix.

$ fsck.minix -f ours.img
Inode 7 is marked as unused, but file '/OTHER.BIN' uses it
Inode 7 is used, but marked as unused in the bitmap.

Every MinixFs image this project has ever produced said that, about its last inode.

What the convention actually is

Read off mkfs.minix rather than off a document. A fresh volume, all three versions:

v1: firstdatazone=10  imap bits=[0, 1]  zmap bits=[0, 1]
v2: firstdatazone=15  imap bits=[0, 1]  zmap bits=[0, 1]
v3: firstdatazone=15  imap bits=[0, 1]  zmap bits=[0, 1]

Bit 0 of the inode bitmap is reserved for the inode number that means "none", and inode N is bit N — hence bits 0 and 1 on a fresh volume: the reserved one and the root. The zone bitmap works the same way and covers the data zones only, counting from the first of them: absolute zone Z is bit Z - firstdatazone + 1. The metadata zones below firstdatazone are not in the map at all.

MinixV1 and MinixV2 do both correctly. MinixFs did neither.

ours correct
inode bitmap bit N-1 bit N
zone bitmap bit Z (absolute) bit Z - firstdatazone + 1
zones below firstdatazone marked used not in the map

Why only the inode half was reported

The inode half is off by one, so the last inode's bit falls off the end — one complaint per volume, every volume.

The zone half never showed. Our writer sizes a volume to fit exactly, so every zone is in use, and "every bit set" is right by accident whichever way the bits are counted. It only appears once something is free — after an in-place remove, say. With the fix in place a volume at 29 of 30 zones checks clean; before it, the free zone lands under the wrong bit.

Why nothing caught it

The writer, the in-place modifier and the block mover all used the same wrong convention, so they agreed with each other perfectly — and so did the test fixtures, which built their own images with disk[imapOff] = 0x01 and a comment asserting "per spec, bit 0 of the zone bitmap covers firstDataZone". Every part of the picture was consistent, and none of it was minix. The same shape as the UBIFS name offset, where the hand-built fixtures carried the bug they were meant to catch.

Fixed across MinixFsWriter, MinixFsInPlaceModifier and MinixFsBlockMover, with the fixtures corrected against mkfs.minix and a check that makes free space before asking fsck.minix, since that is the state the zone half needs to be visible in.

Also in the block mover

MinixFsBlockMover stopped walking an inode's zone pointers at the first zero (if (ptr == 0) break;). A zero pointer is a hole, not the end of the file, so every pointer behind a hole kept naming a zone that had just been moved.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions