Files
erofs-freebsd-out-tree/tests/results/manual/2026-08-08T2337Z-metadata-vfs/prepare-fixtures.sh
T
2026-08-13 10:44:59 +02:00

278 lines
10 KiB
Bash
Executable File

#!/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 fsck.erofs dump.erofs python3 sha256sum; do
command -v "$tool" >/dev/null 2>&1 || {
echo "missing tool: $tool" >&2
exit 1
}
done
test "$(id -u)" -eq 0 || {
echo "root is required to create device-node fixtures" >&2
exit 1
}
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/inline" \
"$fixture_dir/special" \
"$fixture_dir/namei/alpha/bravo/charlie" \
"$fixture_dir/namei/alpha/sibling" \
"$fixture_dir/namei/wide" \
"$fixture_dir/pager" \
"$fixture_dir/nfs/basic/subdir" \
"$fixture_dir/nlink/subdir" \
"$artifact_dir"
printf 'inline-tail-payload-0123456789\n' > "$fixture_dir/inline/inline.txt"
printf 'special target\n' > "$fixture_dir/special/target"
ln -s target "$fixture_dir/special/link"
mknod "$fixture_dir/special/char-large" c 2748 344865
mknod "$fixture_dir/special/block-large" b 2748 344865
mkfifo "$fixture_dir/special/fifo"
printf 'cold nested lookup payload\n' > \
"$fixture_dir/namei/alpha/bravo/charlie/payload.txt"
printf 'repeat lookup payload\n' > \
"$fixture_dir/namei/alpha/bravo/repeat.txt"
printf 'sibling marker\n' > "$fixture_dir/namei/alpha/sibling/marker.txt"
index=0
while [ "$index" -lt 320 ]; do
name=$(printf 'entry-%03d-abcdefghijklmnopqrstuvwxyz.txt' "$index")
printf 'wide entry %03d\n' "$index" > "$fixture_dir/namei/wide/$name"
index=$((index + 1))
done
python3 - "$fixture_dir" <<'PY'
from pathlib import Path
import sys
root = Path(sys.argv[1])
data = bytes(((index * 131 + 17) ^ (index >> 3)) & 0xff
for index in range(5 * 4096 + 731))
(root / "pager" / "pager.bin").write_bytes(data)
(root / "nfs" / "basic" / "regular.txt").write_text(
"stable file handle payload\n", encoding="ascii")
(root / "nfs" / "basic" / "subdir" / "child.txt").write_text(
"child\n", encoding="ascii")
PY
ln -s regular.txt "$fixture_dir/nfs/basic/link-to-regular"
mkfifo "$fixture_dir/nfs/basic/test.fifo"
printf 'single\n' > "$fixture_dir/nlink/single.txt"
printf 'hardlinked\n' > "$fixture_dir/nlink/hard-a.txt"
ln "$fixture_dir/nlink/hard-a.txt" "$fixture_dir/nlink/hard-b.txt"
printf 'child\n' > "$fixture_dir/nlink/subdir/child.txt"
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 11111111-2222-3333-4444-555555555551 inline.erofs inline
build_image 11111111-2222-3333-4444-555555555552 \
special-compact.erofs special -E force-inode-compact
build_image 11111111-2222-3333-4444-555555555553 \
special-extended.erofs special -E force-inode-extended
build_image 11111111-2222-3333-4444-555555555554 namei-base.erofs namei
build_image 11111111-2222-3333-4444-555555555555 \
pager-plain.erofs pager -E noinline_data
build_image 11111111-2222-3333-4444-555555555556 \
pager-lz4.erofs pager -z lz4
build_image aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeee1 nfs-a.erofs nfs
build_image aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeee2 nfs-b.erofs nfs
build_image 11111111-2222-3333-4444-555555555557 \
nlink.erofs nlink -E force-inode-compact
ARTIFACT_DIR="$artifact_dir" python3 <<'PY'
from pathlib import Path
import hashlib
import os
import struct
artifact_dir = Path(os.environ["ARTIFACT_DIR"])
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 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 update_superblock_checksum(image):
polynomial = 0x82F63B78
checksum = 0xFFFFFFFF
put_u32(image, 1028, 0)
for byte in image[1024:4096]:
checksum ^= byte
for _ in range(8):
checksum = (checksum >> 1) ^ (
polynomial if checksum & 1 else 0)
put_u32(image, 1028, checksum & 0xFFFFFFFF)
def inode_offset(image, nid):
block_bits = image[1036]
meta_blkaddr = u32(image, 1064)
return (meta_blkaddr << block_bits) + (nid << 5)
def compact_inode(image, nid):
offset = inode_offset(image, nid)
inode_format = u16(image, offset)
assert (inode_format & 1) == 0
return offset, (inode_format >> 1) & 7, u32(image, offset + 8)
def inline_dir_entries(image, nid):
inode = inode_offset(image, nid)
inode_format = u16(image, inode)
inode_size = 64 if inode_format & 1 else 32
layout = (inode_format >> 1) & 7
size = (struct.unpack_from("<Q", image, inode + 8)[0]
if inode_size == 64 else u32(image, inode + 8))
assert layout == 2 and u16(image, inode + 2) == 0
data = inode + inode_size
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, nameoff = struct.unpack_from("<QH", image, entry)
endoff = (u16(image, data + (index + 1) * 12 + 8)
if index + 1 < count else size)
name = bytes(image[data + nameoff:data + endoff]).split(b"\0", 1)[0]
assert name
entries.append((child_nid, nameoff, endoff, name))
return inode, data, size, entries
def write_image(name, image):
update_superblock_checksum(image)
(artifact_dir / name).write_bytes(image)
inline = bytearray((artifact_dir / "inline.erofs").read_bytes())
_, _, _, inline_root = inline_dir_entries(inline, u16(inline, 1038))
inline_nid = next(nid for nid, _, _, name in inline_root if name == b"inline.txt")
inline_inode, layout, inline_size = compact_inode(inline, inline_nid)
assert layout == 2 and u16(inline, inline_inode + 2) == 0
inline_cross_xattr_icount = 695
inline_xattr_size = 12 + 4 * (inline_cross_xattr_icount - 1)
inline_data_off = inline_inode + 32 + inline_xattr_size
block_size = 1 << inline[1036]
assert inline_data_off % block_size + inline_size > block_size
inline_cross = bytearray(inline)
put_u16(inline_cross, inline_inode + 2, inline_cross_xattr_icount)
write_image("inline-cross-block.erofs", inline_cross)
namei = bytearray((artifact_dir / "namei-base.erofs").read_bytes())
root_nid = u16(namei, 1038)
root_inode, root_data, root_size, root_entries = inline_dir_entries(
namei, root_nid)
wide_nid = next(nid for nid, _, _, name in root_entries if name == b"wide")
wide_inode, wide_layout, _ = compact_inode(namei, wide_nid)
assert wide_layout == 2
block_bits = namei[1036]
wide_block = u32(namei, wide_inode + 16) << block_bits
wide_first_nameoff = u16(namei, wide_block + 8)
wide_count = wide_first_nameoff // 12
wide_last_nameoff = u16(namei, wide_block + (wide_count - 1) * 12 + 8)
padding_nul = namei.index(0, wide_block + wide_last_nameoff,
wide_block + (1 << block_bits))
nonzero_padding = bytearray(namei)
nonzero_padding[padding_nul + 1:padding_nul + 9] = b"PAD!ERO!"
write_image("namei-padding-nonzero.erofs", nonzero_padding)
short_block = bytearray(namei)
put_u32(short_block, root_inode + 8, 8)
write_image("namei-corrupt-short.erofs", short_block)
bad_nameoff = bytearray(namei)
first_nameoff = u16(bad_nameoff, root_data + 8)
put_u16(bad_nameoff, root_data + 12 + 8, first_nameoff)
write_image("namei-corrupt-nameoff.erofs", bad_nameoff)
bad_name = bytearray(namei)
slash_offset = wide_block + wide_last_nameoff + 5
assert bad_name[slash_offset] not in (0, ord("/"))
bad_name[slash_offset] = ord("/")
write_image("namei-corrupt-name.erofs", bad_name)
nlink = bytearray((artifact_dir / "nlink.erofs").read_bytes())
nlink_root_nid = u16(nlink, 1038)
_, _, _, nlink_entries = inline_dir_entries(nlink, nlink_root_nid)
single_nid = next(nid for nid, _, _, name in nlink_entries
if name == b"single.txt")
single_inode, _, _ = compact_inode(nlink, single_nid)
nlink1 = bytearray(nlink)
put_u16(nlink1, single_inode, u16(nlink1, single_inode) | 0x10)
put_u16(nlink1, single_inode + 6, 0x1234)
write_image("nlink1-patched.erofs", nlink1)
with (artifact_dir / "fixture-evidence.txt").open("w", encoding="ascii") as out:
out.write(f"inline_nid={inline_nid} inode_off={inline_inode} "
f"inode_blockoff={inline_inode % block_size} "
f"xattr_icount=0->{inline_cross_xattr_icount} "
f"inline_data_blockoff={inline_data_off % block_size} "
f"inline_size={inline_size}\n")
out.write(f"wide_nid={wide_nid} block={wide_block >> block_bits} "
f"dirents={wide_count} last_nameoff={wide_last_nameoff} "
f"padding_patch={padding_nul + 1 - wide_block}:"
f"{padding_nul + 9 - wide_block}\n")
out.write(f"nlink1_nid={single_nid} i_format_bit4=1 i_nb=0x1234\n")
for image_name in ("special-compact.erofs", "special-extended.erofs"):
image = bytearray((artifact_dir / image_name).read_bytes())
special_root = u16(image, 1038)
_, _, _, entries = inline_dir_entries(image, special_root)
out.write(image_name + "\n")
for nid, _, _, name in entries:
if name not in (b"char-large", b"block-large", b"fifo"):
continue
offset = inode_offset(image, nid)
out.write(f" {name.decode()} nid={nid} inode_size="
f"{64 if u16(image, offset) & 1 else 32} "
f"raw_u={u32(image, offset + 16):#010x}\n")
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
fsck.erofs -d1 "$artifact_dir/namei-padding-nonzero.erofs"
dump.erofs --cat --path=/wide/entry-079-abcdefghijklmnopqrstuvwxyz.txt \
"$artifact_dir/namei-padding-nonzero.erofs"
cat "$artifact_dir/fixture-evidence.txt"
cat "$artifact_dir/SHA256SUMS"