#!/bin/sh set -eu script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) output=${1:?usage: prepare_g1_fixtures.sh OUTPUT} source_dir="$output/source" base_dir="$output/base" image_dir="$output/images" expected_dir="$output/expected" for tool in mkfs.erofs fsck.erofs dump.erofs python3 sha256sum stat; 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 "$output" || { echo "output already exists: $output" >&2 exit 1 } mkdir -p \ "$source_dir/compact/dir" \ "$source_dir/compact/dotdir" \ "$source_dir/extended" \ "$source_dir/flat/very/long/path/to/a/deeply/nested/deterministic/target/directory" \ "$source_dir/inline" \ "$base_dir" "$expected_dir" SOURCE_DIR="$source_dir" EXPECTED_DIR="$expected_dir" python3 <<'PY' from pathlib import Path import os source = Path(os.environ["SOURCE_DIR"]) expected = Path(os.environ["EXPECTED_DIR"]) def write_pattern(path: Path, size: int, seed: int) -> None: path.parent.mkdir(parents=True, exist_ok=True) with path.open("wb") as out: offset = 0 while offset < size: count = min(65536, size - offset) out.write(bytes( (((offset + index) * 131 + seed) ^ ((offset + index) >> 5)) & 0xff for index in range(count) )) offset += count compact = source / "compact" (compact / "root.txt").write_text("repo22 G1 root payload\n", encoding="ascii") write_pattern(compact / "testfile.txt", 65537, 1) (compact / "small.txt").write_text("compact inode payload\n", encoding="ascii") (compact / "single.txt").write_text("single-link payload\n", encoding="ascii") (compact / "hard-a.txt").write_text("hard-link payload\n", encoding="ascii") (compact / "dotdir" / "child.txt").write_text("dot directory child\n", encoding="ascii") (compact / "invalid-target.txt").write_text("invalid nid target\n", encoding="ascii") (compact / "special-target.txt").write_text("special target\n", encoding="ascii") extended = source / "extended" write_pattern(extended / "large-file.bin", 2 * 1024 * 1024 + 731, 2) (extended / "huge-sparse.dat").write_bytes(b"x") (extended / "oversize.dat").write_bytes(b"x") flat = source / "flat" (flat / "small.txt").write_text("flat plain small payload\n", encoding="ascii") write_pattern(flat / "medium.dat", 100 * 1024 + 17, 3) write_pattern(flat / "large.bin", 10 * 1024 * 1024 + 31, 4) write_pattern(flat / "data.txt", 256 * 1024 + 19, 5) write_pattern(flat / "random-test.bin", 1024 * 1024, 6) write_pattern(flat / "huge-data.bin", 100 * 1024 * 1024 + 257, 7) (flat / "very" / "long" / "path" / "to" / "a" / "deeply" / "nested" / "deterministic" / "target" / "directory" / "file.txt").write_text( "long symlink target payload\n", encoding="ascii" ) inline = source / "inline" (inline / "tiny.txt").write_text("inline data\n", encoding="ascii") (inline / "empty.txt").write_bytes(b"") (inline / "inline.txt").write_bytes(b"inline-tail-payload-0123456789\n") write_pattern(inline / "tailpacked.dat", 5000, 8) write_pattern(inline / "file-4095.dat", 4095, 9) write_pattern(inline / "file-4096.dat", 4096, 10) write_pattern(inline / "file-4097.dat", 4097, 11) (inline / "target.txt").write_text("short symlink target payload\n", encoding="ascii") for source_path, offset, length, output_name in ( (flat / "medium.dat", 10 * 4096, 5 * 4096, "medium-10-5.bin"), (flat / "random-test.bin", 0, 10 * 1024, "random-start.bin"), (flat / "random-test.bin", 500 * 1024, 10 * 1024, "random-mid.bin"), (flat / "random-test.bin", 1000 * 1024, 24 * 1024, "random-end.bin"), (flat / "huge-data.bin", 50 * 1024 * 1024, 5 * 1024 * 1024, "huge-sample.bin"), ): with source_path.open("rb") as handle: handle.seek(offset) data = handle.read(length) if len(data) != length: raise RuntimeError(f"short expected range from {source_path}") (expected / output_name).write_bytes(data) (expected / "large-hole-zero-64k.bin").write_bytes(bytes(65536)) (expected / "large-hole-description.txt").write_text( "path=/huge-sparse.dat size=4294971393 content=all-zero\n", encoding="ascii" ) PY ln "$source_dir/compact/testfile.txt" "$source_dir/compact/dir/testfile.txt" ln "$source_dir/compact/hard-a.txt" "$source_dir/compact/hard-b.txt" ln -s special-target.txt "$source_dir/compact/special-link" mknod "$source_dir/compact/char-large" c 2748 344865 mknod "$source_dir/compact/block-large" b 2748 344865 mkfifo "$source_dir/compact/fifo" ln -s target.txt "$source_dir/inline/link1" ln -s /nonexistent/repo22-g1-target "$source_dir/inline/brokenlink" ln -s very/long/path/to/a/deeply/nested/deterministic/target/directory/file.txt \ "$source_dir/flat/longlink" find "$source_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" "$@" "$base_dir/$image" "$source_dir/$source" } build_image 31000000-0000-0000-0000-000000000001 \ compact.erofs compact -E force-inode-compact build_image 31000000-0000-0000-0000-000000000002 \ extended.erofs extended -E force-inode-extended build_image 31000000-0000-0000-0000-000000000003 \ flat.erofs flat -E noinline_data,force-inode-compact build_image 31000000-0000-0000-0000-000000000004 \ inline.erofs inline -E force-inode-compact python3 "$script_dir/g1_fixtures.py" \ --compact "$base_dir/compact.erofs" \ --extended "$base_dir/extended.erofs" \ --flat "$base_dir/flat.erofs" \ --inline "$base_dir/inline.erofs" \ --output "$image_dir" for image in compact.erofs extended.erofs flat.erofs inline.erofs \ compact-nlink1.erofs inline-zero.erofs extended-large-hole.erofs; do fsck.erofs "$image_dir/$image" >/dev/null done printf '%s\n' \ '48-bit fixtures are field/CRC checked by g1_fixtures.py;' \ 'production fsck.erofs 1.8.6 does not recognize incompat feature 0x80.' dump.erofs --cat --path=/small.txt "$image_dir/compact.erofs" | cmp - "$source_dir/compact/small.txt" dump.erofs --cat --path=/large-file.bin "$image_dir/extended.erofs" | cmp - "$source_dir/extended/large-file.bin" dump.erofs --cat --path=/medium.dat "$image_dir/flat.erofs" | cmp - "$source_dir/flat/medium.dat" dump.erofs --cat --path=/tailpacked.dat "$image_dir/inline.erofs" | cmp - "$source_dir/inline/tailpacked.dat" ( cd "$output" find source expected -type f -print0 | sort -z | xargs -0 sha256sum ) > "$output/SOURCE-SHA256SUMS" ( cd "$output" find source -type l -print | sort | while IFS= read -r path; do printf '%s -> %s\n' "$path" "$(readlink "$path")" done find source -type p -printf '%p type=fifo\n' for kind in block:b char:c; do label=${kind%:*} type=${kind#*:} find source -type "$type" -print | sort | while IFS= read -r path; do set -- $(stat -c '%t %T' "$path") printf '%s type=%s major=%d minor=%d\n' \ "$path" "$label" "$((0x$1))" "$((0x$2))" done done ) > "$output/SOURCE-METADATA" ( cd "$output" sha256sum SOURCE-METADATA ) > "$output/SOURCE-METADATA.sha256" cat "$image_dir/fixture-evidence.txt" cat "$output/SOURCE-SHA256SUMS" cat "$output/SOURCE-METADATA" cat "$image_dir/IMAGE-SHA256SUMS"