test code v1

This commit is contained in:
2026-08-13 10:44:59 +02:00
commit f3b1165f19
301 changed files with 37885 additions and 0 deletions
@@ -0,0 +1,271 @@
#!/bin/sh
set -eu
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
fixture_dir=${FIXTURE_DIR:-"$script_dir/fixture"}
artifact_dir=${ARTIFACT_DIR:-"$script_dir/artifacts"}
for tool in mkfs.erofs python3 sha256sum; do
command -v "$tool" >/dev/null 2>&1 || {
echo "missing tool: $tool" >&2
exit 1
}
done
test ! -e "$fixture_dir" || {
echo "fixture directory already exists: $fixture_dir" >&2
exit 1
}
test ! -e "$artifact_dir" || {
echo "artifact directory already exists: $artifact_dir" >&2
exit 1
}
mkdir -p "$fixture_dir/fallback" "$fixture_dir/oversize" \
"$fixture_dir/extent" "$fixture_dir/large/huge" "$artifact_dir"
FIXTURE_DIR="$fixture_dir" python3 <<'PY'
from pathlib import Path
import os
root = Path(os.environ["FIXTURE_DIR"])
(root / "fallback" / "root.txt").write_text(
"48-bit fallback root\n", encoding="ascii")
(root / "oversize" / "big.dat").write_bytes(b"x")
(root / "extent" / "hole.dat").write_bytes(b"\0" * (1024 * 1024))
(root / "large" / "huge" / "anchor.txt").write_text(
"large directory anchor\n", encoding="ascii")
for index in range(400):
(root / "large" / "huge" /
f"entry-{index:03d}-abcdefghijklmnopqrstuvwxyz.txt").write_text(
f"entry {index:03d}\n", encoding="ascii")
PY
find "$fixture_dir" -exec touch -h -t 197001010000.00 {} +
build_image()
{
uuid=$1
image=$2
source=$3
shift 3
mkfs.erofs -d0 -x-1 -T0 --all-time --all-root --workers=1 \
-U "$uuid" "$@" "$artifact_dir/$image" "$fixture_dir/$source"
}
build_image 22222222-3333-4444-5555-666666666650 \
fallback-base.erofs fallback -E force-inode-compact
build_image 22222222-3333-4444-5555-666666666651 \
oversize-base.erofs oversize -E force-inode-extended
build_image 22222222-3333-4444-5555-666666666652 \
extent-base.erofs extent -E legacy-compress,force-inode-extended -z lz4
build_image 22222222-3333-4444-5555-666666666653 \
large-dir-base.erofs large -E force-inode-extended
ARTIFACT_DIR="$artifact_dir" python3 <<'PY'
from pathlib import Path
import hashlib
import os
import struct
artifact_dir = Path(os.environ["ARTIFACT_DIR"])
SUPER = 1024
FEATURE_INCOMPAT = SUPER + 80
ROOTNID_2B = SUPER + 14
BLOCKS_LO = SUPER + 36
META_BLKADDR = SUPER + 40
ROOTNID_8B = SUPER + 112
EROFS_FEATURE_INCOMPAT_48BIT = 0x80
EROFS_INODE_COMPRESSED_FULL = 1
Z_EROFS_ADVISE_EXTENTS = 0x1
Z_EROFS_EXTENT_RECSZ_16 = 0x4
HOLE_SIZE = 5 * 1024 * 1024 * 1024 + 4096
HUGE_DIR_BLOCKS = (1 << 31) + 1
def u16(image, offset):
return struct.unpack_from("<H", image, offset)[0]
def u32(image, offset):
return struct.unpack_from("<I", image, offset)[0]
def u64(image, offset):
return struct.unpack_from("<Q", image, offset)[0]
def put_u16(image, offset, value):
struct.pack_into("<H", image, offset, value)
def put_u32(image, offset, value):
struct.pack_into("<I", image, offset, value)
def put_u64(image, offset, value):
struct.pack_into("<Q", image, offset, value)
def update_superblock_checksum(image):
polynomial = 0x82F63B78
checksum = 0xFFFFFFFF
block_size = 1 << image[SUPER + 12]
put_u32(image, SUPER + 4, 0)
for byte in image[SUPER:block_size]:
checksum ^= byte
for _ in range(8):
checksum = (checksum >> 1) ^ (
polynomial if checksum & 1 else 0)
put_u32(image, SUPER + 4, checksum & 0xFFFFFFFF)
def inode_offset(image, nid):
block_bits = image[SUPER + 12]
return (u32(image, META_BLKADDR) << block_bits) + (nid << 5)
def inode_info(image, nid):
offset = inode_offset(image, nid)
inode_format = u16(image, offset)
inode_size = 64 if inode_format & 1 else 32
size = u64(image, offset + 8) if inode_size == 64 else u32(
image, offset + 8)
xattr_count = u16(image, offset + 2)
xattr_size = 0 if xattr_count == 0 else 12 + 4 * (xattr_count - 1)
layout = (inode_format >> 1) & 7
return offset, inode_format, inode_size, xattr_size, layout, size
def directory_entries(image, nid):
offset, _, inode_size, xattr_size, layout, size = inode_info(image, nid)
block_size = 1 << image[SUPER + 12]
assert 0 < size <= block_size
if layout == 2:
data = offset + inode_size + xattr_size
elif layout == 0:
data = u32(image, offset + 16) << image[SUPER + 12]
else:
raise AssertionError(f"unsupported directory layout {layout}")
first_nameoff = u16(image, data + 8)
assert first_nameoff >= 12 and first_nameoff % 12 == 0
count = first_nameoff // 12
entries = []
for index in range(count):
entry = data + index * 12
child_nid = u64(image, entry)
nameoff = u16(image, entry + 8)
endoff = (u16(image, entry + 20)
if index + 1 < count else size)
name = bytes(image[data + nameoff:data + endoff]).split(b"\0", 1)[0]
assert name
entries.append((name, child_nid))
return entries
def child_nid(image, parent_nid, name):
return next(nid for entry_name, nid in directory_entries(image, parent_nid)
if entry_name == name)
def convert_inline_directory_to_plain(image, nid):
offset, inode_format, inode_size, xattr_size, layout, size = inode_info(
image, nid)
assert layout == 2 and inode_size == 64 and size > 0
block_size = 1 << image[SUPER + 12]
tail_size = size % block_size
assert tail_size > 0
raw_block = u32(image, offset + 16)
full_size = size - tail_size
inline_offset = offset + inode_size + xattr_size
directory_data = bytes(
image[raw_block * block_size:raw_block * block_size + full_size] +
image[inline_offset:inline_offset + tail_size])
assert len(directory_data) == size
new_raw_block = (len(image) + block_size - 1) // block_size
image.extend(b"\0" * (new_raw_block * block_size - len(image)))
image.extend(directory_data)
image.extend(b"\0" * (-len(image) % block_size))
put_u16(image, offset, inode_format & ~(7 << 1))
put_u32(image, offset + 16, new_raw_block)
put_u32(image, BLOCKS_LO, len(image) // block_size)
assert inode_info(image, nid)[4] == 0
return new_raw_block, len(image) // block_size
def write_image(name, image):
update_superblock_checksum(image)
(artifact_dir / name).write_bytes(image)
evidence = []
fallback = bytearray((artifact_dir / "fallback-base.erofs").read_bytes())
fallback_root = u16(fallback, ROOTNID_2B)
fallback_blocks = u32(fallback, BLOCKS_LO)
assert fallback_root != 0 and u64(fallback, ROOTNID_8B) == 0
put_u32(fallback, FEATURE_INCOMPAT,
u32(fallback, FEATURE_INCOMPAT) | EROFS_FEATURE_INCOMPAT_48BIT)
put_u64(fallback, ROOTNID_8B, 0)
write_image("fallback-48bit-root2.erofs", fallback)
evidence.append(
f"fallback rootnid_2b={fallback_root} rootnid_8b=0 "
f"blocks_lo={fallback_blocks} union=0x{u16(fallback, ROOTNID_2B):04x}")
oversize = bytearray((artifact_dir / "oversize-base.erofs").read_bytes())
oversize_root = u16(oversize, ROOTNID_2B)
oversize_nid = child_nid(oversize, oversize_root, b"big.dat")
oversize_off, oversize_format, oversize_isize, _, _, _ = inode_info(
oversize, oversize_nid)
assert oversize_format & 1 and oversize_isize == 64
put_u64(oversize, oversize_off + 8, 1 << 63)
write_image("extended-size-bit63.erofs", oversize)
evidence.append(
f"oversize nid={oversize_nid} inode_off={oversize_off} "
f"i_size=0x{u64(oversize, oversize_off + 8):016x}")
extent = bytearray((artifact_dir / "extent-base.erofs").read_bytes())
extent_root = u16(extent, ROOTNID_2B)
extent_nid = child_nid(extent, extent_root, b"hole.dat")
extent_off, extent_format, extent_isize, extent_xattr, extent_layout, _ = \
inode_info(extent, extent_nid)
assert extent_format & 1 and extent_isize == 64
assert extent_layout == EROFS_INODE_COMPRESSED_FULL
header = (extent_off + extent_isize + extent_xattr + 7) & ~7
record = (header + 8 + 15) & ~15
root_off = inode_offset(extent, extent_root)
assert not (header < root_off + 64 and root_off < record + 16)
put_u64(extent, extent_off + 8, HOLE_SIZE)
struct.pack_into("<IHH", extent, header, 1,
Z_EROFS_ADVISE_EXTENTS | Z_EROFS_EXTENT_RECSZ_16, 0)
struct.pack_into("<IIII", extent, record, 0, 0, 0, 0)
write_image("extent-hole-5g.erofs", extent)
evidence.append(
f"extent-hole nid={extent_nid} inode_off={extent_off} size={HOLE_SIZE} "
f"header={header} record={record} extents=1 recsize=16 plen=0 lstart=0")
large = bytearray((artifact_dir / "large-dir-base.erofs").read_bytes())
large_root = u16(large, ROOTNID_2B)
large_nid = child_nid(large, large_root, b"huge")
large_off, large_format, large_isize, _, _, _ = inode_info(large, large_nid)
assert large_format & 1 and large_isize == 64
large_raw_block, large_image_blocks = convert_inline_directory_to_plain(
large, large_nid)
large_size = HUGE_DIR_BLOCKS * (1 << large[SUPER + 12])
put_u64(large, large_off + 8, large_size)
write_image("large-dir-intmax.erofs", large)
evidence.append(
f"large-dir nid={large_nid} inode_off={large_off} size={large_size} "
f"blocks={HUGE_DIR_BLOCKS} last_block={HUGE_DIR_BLOCKS - 1} "
f"layout=flat-plain raw_block={large_raw_block} "
f"image_blocks={large_image_blocks}")
(artifact_dir / "fixture-evidence.txt").write_text(
"\n".join(evidence) + "\n", encoding="ascii")
with (artifact_dir / "SHA256SUMS").open("w", encoding="ascii") as sums:
for path in sorted(artifact_dir.glob("*.erofs")):
sums.write(f"{hashlib.sha256(path.read_bytes()).hexdigest()} "
f"{path.name}\n")
PY
cat "$artifact_dir/fixture-evidence.txt"
cat "$artifact_dir/SHA256SUMS"