# squashfs-tools -- mksquashfs packs a directory tree into a compressed # read-only .sqfs image, unsquashfs reads and extracts one. Like # dosfstools/mtools/e2fsprogs it works on a plain FILE, which is what makes it # usable on a machine with no block layer. # # THE READ SIDE IS GREEN AND IT IS REAL WORK. unsquashfs reads the superblock # and lists the tree of data/squashfs-sample.sqfs -- an image built by UPSTREAM # mksquashfs on the build host, not by us -- so those cases prove on-disk-format # compatibility and gzip metadata decompression on target, independently of # whether our own mksquashfs can write. mksquashfs itself reports its version # and its compiled-in compressor set, which is this image's only on-target proof # that the recipe's PACKAGECONFIG took. # # THE WRITE SIDE IS RED, IS NOT EXECUTED BY DEFAULT, AND MUST STAY RED. # Packing and extracting both need the reader/inflator/deflator thread pool, and # pthread_create traps on this arm (brintos/linux-in-the-browser#85 -- the # coroutinized thread-start frame parks under the native spawn machinery and the # orphaned handle trips "set-over-set" at the next park). Unlike md5deep and # zstd there is no threadless build mode to select: upstream has no such knob # and the thread pipeline is the program. # # They are reported without being run because running them does not just fail # -- it takes the whole selftest run down with it. The trap poisons a lane that # a later, unrelated exec then hits inside the kernel # (wasm32_user_exec_load -> __hwjs_clear_parked), and because the runner buffers # each suite's output and prints it only on completion, a machine death here # discards every result from this file AND every suite after it. Set # BST_SQUASHFS_WRITE=1 to actually exercise them (expect the run to die); the # default reports them as the known blocker instead of destroying the capture. . "$BST_LIB" require unsquashfs squashfs-tools/unsquashfs-present require mksquashfs squashfs-tools/mksquashfs-present T=${TMPDIR:-/tmp}/bst-sqfs.$$ mkdir -p "$T" || exit 0 trap 'rm -rf "$T"' EXIT FIX=$BST_DATA/squashfs-sample.sqfs # BRINTOS-WORKAROUND: linux-in-the-browser#85 remove — the cases skipped with this reason are MASKED, so they will never flip green by themselves; drop the skip and let them run once the issue closes. B85="blocked: pthread_create traps (hwjs-user-rt set-over-set) -- brintos/linux-in-the-browser#85" # assert on OUTPUT only: the compressor list is printed on the error path of an # unknown -comp, which exits non-zero by design, so check_out (which also # demands rc 0) would conflate the two. has() { case "$3" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "want '$2' in compressor list, got: $(_bst_1line "$3")" ;; esac; } # assert on OUTPUT only, ignoring rc: unsquashfs -version prints its banner and # then exits 1, so check_out would report a working binary as broken. msg() { _i=$1; _n=$2; shift 2; _o=$("$@" 2>&1) case "$_o" in *"$_n"*) bst_ok "$_i" ;; *) bst_notok "$_i" "want '$_n' got: $(_bst_1line "$_o")" ;; esac; } hasnt() { case "$3" in *"$2"*) bst_notok "$1" "'$2' is compiled in, but this image does not build it" ;; *) bst_ok "$1" ;; esac; } # ------------------------------------------------- both binaries load and run # mksquashfs sizes its queues from sysconf(_SC_PHYS_PAGES) before it even # handles -version; sysinfo(2) is ENOSYS here so that reads 0 and upstream # aborts with "requires more physical memory than is available". The recipe # patches it to read /proc/meminfo, so this case is also the on-target check # that the patch is present and working (brintos/linux/issues/26). check_out squashfs-tools/mksquashfs-version "mksquashfs version 4.6.1" mksquashfs -version msg squashfs-tools/unsquashfs-version "unsquashfs version 4.6.1" unsquashfs -version # ------------------------------------------------------- compiled-in backends # An unknown -comp makes mksquashfs print the compressors it was built with, # inside the option-parse loop. The two negative cases are the point: without # them a stock PACKAGECONFIG (which also builds lzo and zstd) would pass the # positive four and the recipe's restriction would go unverified. COMP=$(mksquashfs "$T" "$T/x.sqfs" -comp no-such-compressor 2>&1) has squashfs-tools/comp-gzip "gzip" "$COMP" has squashfs-tools/comp-lz4 "lz4" "$COMP" has squashfs-tools/comp-xz "xz" "$COMP" has squashfs-tools/comp-lzma "lzma" "$COMP" hasnt squashfs-tools/comp-no-lzo "lzo" "$COMP" hasnt squashfs-tools/comp-no-zstd "zstd" "$COMP" rm -f "$T/x.sqfs" # --------------------------------------------- reading an image we did not write check_out squashfs-tools/superblock-valid "Found a valid SQUASHFS 4:0 superblock" unsquashfs -s "$FIX" check_out squashfs-tools/superblock-comp "Compression gzip" unsquashfs -s "$FIX" # Listing walks the directory table, which is itself gzip-compressed ("Inodes # are compressed"), so this is what proves the zlib backend decompresses on # target rather than merely being linked in. check_out squashfs-tools/list-root "squashfs-root" unsquashfs -l "$FIX" check_out squashfs-tools/list-nested "squashfs-root/fixture.txt" unsquashfs -l "$FIX" # A control: a non-squashfs file must be rejected. Without it, a reader that # answered plausibly for any input would sweep every case above. # # Match the full "Found a valid ..." phrase, not "valid SQUASHFS" -- the # REJECTION reads "Can't find a valid SQUASHFS superblock on ...", which # contains the shorter substring, so the loose form asserted the opposite of # what it looks like it asserts. dd if=/dev/zero of="$T/zeroes" bs=1024 count=64 2>/dev/null case "$(unsquashfs -s "$T/zeroes" 2>&1)" in *"Found a valid SQUASHFS"*) bst_notok squashfs-tools/reject-non-sqfs "64 KiB of zeroes accepted as a filesystem" ;; *) bst_ok squashfs-tools/reject-non-sqfs ;; esac # --------------------------------------------------------------- the write side if [ "${BST_SQUASHFS_WRITE:-0}" = 1 ]; then blocked() { # -- name #85 only when its signature is present case "$3" in *set-over-set*|*"hwjs-user-rt VIOLATION"*|*"Illegal instruction"*|*unreachable*|*"futex facility"*) bst_notok "$1" "$B85 (rc=$2)" ;; *) bst_notok "$1" "rc=$2 $(_bst_1line "$3")" ;; esac } OUT=$(unsquashfs -d "$T/ext" "$FIX" 2>&1); RC=$? if [ "$RC" -eq 0 ] && [ -f "$T/ext/fixture.txt" ]; then check_eq squashfs-tools/extract "brintos squashfs fixture" "$(cat "$T/ext/fixture.txt" 2>/dev/null)" else blocked squashfs-tools/extract "$RC" "$OUT" fi SRC=$T/src; mkdir -p "$SRC/sub" printf 'hello squashfs\n' > "$SRC/a.txt" printf 'second file\n' > "$SRC/sub/b.txt" ln -s a.txt "$SRC/link" OUT=$(mksquashfs "$SRC" "$T/tree.sqfs" -comp gzip -no-progress -all-root 2>&1); RC=$? if [ "$RC" -eq 0 ] && [ -s "$T/tree.sqfs" ]; then bst_ok squashfs-tools/pack else blocked squashfs-tools/pack "$RC" "$OUT"; fi if [ -s "$T/tree.sqfs" ] && unsquashfs -d "$T/back" "$T/tree.sqfs" >/dev/null 2>&1; then a=$(cat "$T/back/a.txt" 2>/dev/null); b=$(cat "$T/back/sub/b.txt" 2>/dev/null) l=$(readlink "$T/back/link" 2>/dev/null) if [ "$a" = "hello squashfs" ] && [ "$b" = "second file" ] && [ "$l" = "a.txt" ]; then bst_ok squashfs-tools/roundtrip else bst_notok squashfs-tools/roundtrip "a='$a' b='$b' link='$l'" fi else bst_notok squashfs-tools/roundtrip "$B85 (no image to read back -- pack blocked)" fi else bst_notok squashfs-tools/extract "$B85 (not executed: it kills the run -- BST_SQUASHFS_WRITE=1 to exercise)" bst_notok squashfs-tools/pack "$B85 (not executed: it kills the run -- BST_SQUASHFS_WRITE=1 to exercise)" bst_notok squashfs-tools/roundtrip "$B85 (not executed: it kills the run -- BST_SQUASHFS_WRITE=1 to exercise)" fi