. "$BST_LIB" # BST-TIMEOUT: 300 # measured elapsed=102s over 69 cases; the default 60 trips require uml_mkcow uml-utilities/present # uml-utilities 20040406 (meta-oe recipes-extended) -- the User-Mode-Linux # userspace tools, ported to the wasm32-hwjs glibc arm by BRINTOS-1394. # # The subject of this suite is the COW-file pair: uml_mkcow lays a # copy-on-write file over a backing image and uml_moo merges one back. Both are # ordinary file manipulation over a documented on-disk header, so they are the # half of this package that can be driven to completion on an image with no UML # instance and no tun device -- which is why the ticket names them as the pass # criterion. # # THE ORACLE IS RIGHT BELOW SECTOR 31 AND WRONG ABOVE IT, so this suite compares # against it in one scenario and against a computed reference in the other. See # uml-utilities_%.bbappend for the mechanism; in one line, uml_moo's bit test is # `data[n] & (1 << off)` with an int 1 and off up to sizeof(unsigned long)*8-1, # which is 63 on the x86-64 build host and 31 here. The build host's shift is # therefore undefined for every sector above 30 and this arm's is not, so the # wasm32 build is the correct one and merge-b-native.expected ships as a # counter-example rather than as a reference. # # Everything the suite compares against was produced by the recipe that # cross-compiled these binaries, from the same sources, at do_install. O=/usr/libexec/uml-utilities-tests t=${TMPDIR:-/tmp}/bst-uml.$$ mkdir -p "$t" # ---------------------------------------------------------------- helpers ---- num() { LC_ALL=C awk "BEGIN{print $1}"; } be32() { od -A n -t u1 -N 4 -j "$2" "$1" | LC_ALL=C awk '{print $1*16777216+$2*65536+$3*256+$4}'; } ofact() { sed -n "s/^$1=//p" "$O/oracle.txt" 2>/dev/null; } gensector() { LC_ALL=C awk -v s="$1" 'BEGIN{c=sprintf("%c",33+s);for(i=0;i<512;i++)printf "%s",c}'; } setbit() { # _bi=$(num "int($2/8)") _cur=$(od -A n -t u1 -N 1 -j "$_bi" "$1" | LC_ALL=C awk '{print $1+0}') _new=$(num "$_cur + 2^($2%8)") printf "$(LC_ALL=C awk "BEGIN{printf \"\\\\%03o\", $_new}")" \ | dd of="$1" bs=1 seek="$_bi" count=1 conv=notrunc status=none } # : mark each sector present and fill its COW data block, with # the geometry read back out of the header the tool just wrote rather than # assumed -- alignment is sysconf(_SC_PAGESIZE) and need not be the oracle's. cowwrite() { _c=$1; shift _align=$(be32 "$_c" 28) _bsz=$(be32 "$_c" 20) _bmoff=$(num "int((4136+$_align-1)/$_align)*$_align") _bmlen=$(num "int((int(($_bsz+511)/512)+7)/8)") _dataoff=$(num "int(($_bmoff+$_bmlen+$_align-1)/$_align)*$_align") dd if=/dev/zero of="$t/bm.bin" bs=1 count="$_bmlen" status=none for _s in "$@"; do setbit "$t/bm.bin" "$_s"; done dd if="$t/bm.bin" of="$_c" bs=1 seek="$_bmoff" conv=notrunc status=none for _s in "$@"; do gensector "$_s" | dd of="$_c" bs=1 seek=$(num "$_dataoff + $_s*512") conv=notrunc status=none done } secbyte() { od -A n -t u1 -N 1 -j $(num "$2*512") "$1" | LC_ALL=C awk '{print $1+0}'; } # --------------------------------------------------------------- packaging ---- for b in uml_mkcow uml_moo uml_mconsole uml_switch uml_watchdog uml_net jailtest; do check uml-utilities/have-$b test -x /usr/bin/$b done check uml-utilities/have-port-helper test -x /usr/lib/uml/port-helper check uml-utilities/have-tunctl test -x /usr/bin/tunctl.uml-utilities # THE NAME IS THE ASSERTION. This package carries tunctl at its 2004 vintage and # the image already ships Jeff Dike's 1.5 at /usr/sbin/tunctl; the stock recipe # would register the old one at /usr/bin/tunctl, which comes FIRST on this # image's PATH. The bbappend renames it instead, and these two cases are what # notice if that ever stops happening -- the second is the same guard # 63-tunctl.test keeps, restated here so the port that would break it is the one # that reports it. check uml-utilities/tunctl-not-shadowing test ! -e /usr/bin/tunctl check_eq uml-utilities/tunctl-resolves-to-sbin /usr/sbin/tunctl "$(command -v tunctl 2>/dev/null)" if [ -f "$O/oracle.txt" ]; then bst_ok uml-utilities/oracle-present else bst_notok uml-utilities/oracle-present "no $O/oracle.txt -- the uml-utilities-tests package is not on this image, so every comparison below is unanchored"; fi # ------------------------------------------------------------------- usage ---- # Each tool reached and printing its own diagnostic: the cheapest evidence that # the binary starts, links and reaches main() on this arm. check_out uml-utilities/mkcow-usage "usage:" sh -c 'uml_mkcow 2>&1 || true' check_out uml-utilities/moo-usage "usage:" sh -c 'uml_moo 2>&1 || true' check_out uml-utilities/mconsole-usage "Usage : uml_mconsole socket-name" sh -c 'uml_mconsole 2>&1 || true' check_out uml-utilities/switch-usage "Usage :" sh -c 'uml_switch -h 2>&1 || true' check_out uml-utilities/net-refuses "bad argument list" sh -c 'uml_net 2>&1 || true' check_out uml-utilities/tunctl-usage "Create:" sh -c '/usr/bin/tunctl.uml-utilities -h 2>&1 || true' # uml_mkcow's usage() exits 2; a tool that printed the text and exited 0 would # pass check_out above while being wrong for any caller that tests $?. check_rc uml-utilities/mkcow-usage-rc 2 uml_mkcow # ------------------------------------------------- the COW header, scenario A -- cp "$O/backing-a.img" "$t/backing-a.img" 2>/dev/null check uml-utilities/backing-a-staged test -s "$t/backing-a.img" check uml-utilities/mkcow-a sh -c "cd '$t' && uml_mkcow cow-a.img backing-a.img" if [ -s "$t/cow-a.img" ]; then check_eq uml-utilities/hdr-magic 1330597709 "$(be32 "$t/cow-a.img" 0)" # 0x4f4f4f4d, "MOOO" check_eq uml-utilities/hdr-version 3 "$(be32 "$t/cow-a.img" 4)" check_eq uml-utilities/hdr-size-hi 0 "$(be32 "$t/cow-a.img" 16)" check_eq uml-utilities/hdr-size-lo 15872 "$(be32 "$t/cow-a.img" 20)" check_eq uml-utilities/hdr-sectorsize 512 "$(be32 "$t/cow-a.img" 24)" check_eq uml-utilities/hdr-cowformat 0 "$(be32 "$t/cow-a.img" 32)" # mtime is `header->mtime = htonl(st_mtime)` -- a 32-bit value assigned into a # 64-bit time_t field -- so the low four bytes carry the big-endian mtime and # the high four are the zero-extension. Both halves are asserted: the high one # is what would go non-zero if this arm's time_t were 32 bits and the four # bytes at 12 became uninitialised padding, which is the single fact that # decides whether the header is comparable across the two machines at all. check_eq uml-utilities/hdr-mtime "$(stat -c %Y "$t/backing-a.img")" "$(be32 "$t/cow-a.img" 8)" check_eq uml-utilities/hdr-mtime-hi 0 "$(be32 "$t/cow-a.img" 12)" # alignment is sysconf(_SC_PAGESIZE), so it is a fact about this platform # rather than about the format. Asserted as a power of two at or above 512 # (below that the bitmap would overlap the header), and reported against the # build host's value rather than required to match it. ua=$(be32 "$t/cow-a.img" 28) if [ -n "$ua" ] && [ "$ua" -ge 512 ] 2>/dev/null && [ "$(num "int(log($ua)/log(2)+0.5)")" = "$(num "log($ua)/log(2)")" ]; then bst_ok uml-utilities/hdr-alignment else bst_notok uml-utilities/hdr-alignment "sysconf(_SC_PAGESIZE) landed in the header as '$ua'; expected a power of two >= 512" fi check_eq uml-utilities/pagesize-vs-native "$(ofact native_pagesize)" "$ua" # The backing path the header records, absolutised by the tool itself. This is # also the one place chdir(2) is exercised inside uml_mkcow -- absolutize() # cds to the backing file's directory and back. check_eq uml-utilities/hdr-backing-path "$t/backing-a.img" \ "$(dd if="$t/cow-a.img" bs=1 skip=36 count=4096 2>/dev/null | tr -d '\000')" # THE HEADER, BYTE FOR BYTE AGAINST x86-64. mtime and the backing path record # where the file sits rather than what the format is and cannot match across # two machines, so both are masked out on both sides; everything else -- # including all 4100 bytes of padding after the path -- must be identical. dd if="$t/cow-a.img" of="$t/hdr.bin" bs=1 count=4136 status=none dd if=/dev/zero of="$t/hdr.bin" bs=1 seek=8 count=8 conv=notrunc status=none dd if=/dev/zero of="$t/hdr.bin" bs=1 seek=36 count=4100 conv=notrunc status=none check_eq uml-utilities/hdr-masked-vs-native "$(ofact header_masked_sha256)" \ "$(sha256sum "$t/hdr.bin" | LC_ALL=C awk '{print $1}')" # The padding after the backing path comes from cow_malloc(), which is a bare # malloc() -- write_cow_header fills the declared fields and writes all 4136 # bytes, so whatever the allocator handed back goes to disk. It is zero here # because the allocation is the program's first and comes off a fresh kernel # page, not because anything initialises it. Asserted so that a future # allocator or a caller that dirties the heap first is caught rather than # silently changing what ships in a COW file. nz=$(dd if="$t/cow-a.img" bs=1 skip=136 count=4000 2>/dev/null | tr -d '\000' | wc -c) check_eq uml-utilities/hdr-padding-zero 0 "$nz" else for c in hdr-magic hdr-version hdr-size-hi hdr-size-lo hdr-sectorsize hdr-cowformat \ hdr-mtime hdr-mtime-hi hdr-alignment pagesize-vs-native hdr-backing-path \ hdr-masked-vs-native hdr-padding-zero; do bst_notok uml-utilities/$c "uml_mkcow produced no COW file, so nothing downstream could be measured" done fi # ------------------------------------------ scenario A: the oracle comparison -- # Sectors 1, 5 and 30 over a 31-sector backing: every bit index stays at or # below 30, the range LP64 still gets right, so the two arms must agree exactly. if [ -s "$t/cow-a.img" ]; then cowwrite "$t/cow-a.img" 1 5 30 check uml-utilities/moo-a sh -c "cd '$t' && uml_moo cow-a.img merged-a.img" if [ -s "$t/merged-a.img" ]; then check uml-utilities/merge-a-vs-native cmp -s "$t/merged-a.img" "$O/merge-a.expected" check_eq uml-utilities/merge-a-sha "$(ofact merge_a_sha256)" \ "$(sha256sum "$t/merged-a.img" | LC_ALL=C awk '{print $1}')" check_eq uml-utilities/merge-a-cow-1 34 "$(secbyte "$t/merged-a.img" 1)" # COW (33+1) check_eq uml-utilities/merge-a-cow-30 63 "$(secbyte "$t/merged-a.img" 30)" # COW (33+30) check_eq uml-utilities/merge-a-back-2 66 "$(secbyte "$t/merged-a.img" 2)" # backing (64+2) else bst_notok uml-utilities/merge-a-vs-native "uml_moo produced no merged image" bst_notok uml-utilities/merge-a-sha "uml_moo produced no merged image" fi fi # ------------------------------- scenario B: where the build host is the wrong -- # The same seven-sector scenario over a 64-sector backing. The reference is # computed from the scenario, not taken from a binary; the native output ships # beside it and this arm must NOT reproduce it. cp "$O/backing-b.img" "$t/backing-b.img" 2>/dev/null check uml-utilities/mkcow-b sh -c "cd '$t' && uml_mkcow cow-b.img backing-b.img" if [ -s "$t/cow-b.img" ]; then cowwrite "$t/cow-b.img" 1 5 30 31 32 33 63 check uml-utilities/moo-b sh -c "cd '$t' && uml_moo cow-b.img merged-b.img" fi if [ -s "$t/merged-b.img" ]; then check uml-utilities/merge-b-correct cmp -s "$t/merged-b.img" "$O/merge-b-correct.expected" # The three sectors the LP64 shift gets wrong, each asserted on its own so a # regression names which one moved. 33+s is the COW fill, 64+s the backing. check_eq uml-utilities/bit32-not-aliased 65 "$(secbyte "$t/merged-b.img" 32)" # COW, native reads backing check_eq uml-utilities/sector37-untouched 101 "$(secbyte "$t/merged-b.img" 37)" # backing, native holes it check_eq uml-utilities/sector62-untouched 126 "$(secbyte "$t/merged-b.img" 62)" # backing, native holes it check_eq uml-utilities/bit31-boundary 64 "$(secbyte "$t/merged-b.img" 31)" # COW (33+31) check_eq uml-utilities/bit63-top 96 "$(secbyte "$t/merged-b.img" 63)" # COW (33+63) # The divergence itself. If this ever passes, either the build host stopped # miscompiling the shift or this arm started -- both are findings, and neither # is caught by the per-sector cases alone once the reference is regenerated. if cmp -s "$t/merged-b.img" "$O/merge-b-native.expected"; then bst_notok uml-utilities/differs-from-native-lp64 \ "this arm reproduced the x86-64 output byte for byte, so the LP64 (1 << off) defect is now present here too -- sizeof(unsigned long) was 4 when the port was measured" else bst_ok uml-utilities/differs-from-native-lp64 fi else for c in merge-b-correct bit32-not-aliased sector37-untouched sector62-untouched \ bit31-boundary bit63-top differs-from-native-lp64; do bst_notok uml-utilities/$c "uml_moo produced no scenario-B merged image" done fi # ----------------------------------------------------- destructive merge (-d) -- # uml_moo -d writes the COW's sectors back into the backing file in place, which # is a different code path: out_fd == back_fd, so the loop skips every sector it # would otherwise copy and rewrites only the ones the COW carries. cp "$O/backing-a.img" "$t/backing-d.img" 2>/dev/null check uml-utilities/mkcow-d sh -c "cd '$t' && uml_mkcow cow-d.img backing-d.img" if [ -s "$t/cow-d.img" ]; then cowwrite "$t/cow-d.img" 1 5 30 check uml-utilities/moo-destructive sh -c "cd '$t' && uml_moo -d cow-d.img" check uml-utilities/merge-d-vs-native cmp -s "$t/backing-d.img" "$O/merge-a.expected" fi # ------------------------------------------------ -b, the backing-file override -- # The COW records an absolute path; -b points the merge at a different file, which # is how a COW created inside a chroot is merged from outside it. cp "$O/backing-a.img" "$t/elsewhere.img" 2>/dev/null touch -r "$t/backing-a.img" "$t/elsewhere.img" 2>/dev/null if [ -s "$t/cow-a.img" ]; then check uml-utilities/moo-b-override sh -c "cd '$t' && uml_moo -b elsewhere.img cow-a.img merged-ovr.img" check uml-utilities/merge-ovr-vs-native cmp -s "$t/merged-ovr.img" "$O/merge-a.expected" # THE ARM THAT USED TO TRAP. uml_moo checks the replacement's size and mtime # against what the COW header records and refuses on either. The mtime message # passed a 64-bit time_t to %ld with no cast, so on this ILP32 arm it died # dereferencing half an integer as the %s pointer -- rc=132 and no output at # all, on the one path whose job is to print. Both refusals are asserted, so # the fix is pinned by the message AND by the exit status the caller sees. cp "$O/backing-a.img" "$t/othertime.img" 2>/dev/null touch -d '2001-02-03 04:05:06' "$t/othertime.img" 2>/dev/null check_rc uml-utilities/moo-b-mtime-rc 1 sh -c "cd '$t' && uml_moo -b othertime.img cow-a.img mt.img" check_out uml-utilities/moo-b-mtime-msg "mtime mismatch" \ sh -c "cd '$t' && uml_moo -b othertime.img cow-a.img mt2.img 2>&1 || true" # The size refusal is the control that made the mtime one a format bug rather # than a platform one: same function, three lines apart, and it always printed. dd if="$t/backing-a.img" of="$t/shorter.img" bs=512 count=30 status=none touch -r "$t/backing-a.img" "$t/shorter.img" 2>/dev/null check_out uml-utilities/moo-b-size-msg "Size mismatch (15872 vs 15360)" \ sh -c "cd '$t' && uml_moo -b shorter.img cow-a.img sz.img 2>&1 || true" fi # -------------------------------------------------------------- error paths ---- # The COW is opened O_EXCL unless -f is given. check_out uml-utilities/mkcow-exclusive "File exists" \ sh -c "cd '$t' && uml_mkcow cow-a.img backing-a.img 2>&1 || true" check uml-utilities/mkcow-force sh -c "cd '$t' && uml_mkcow -f cow-a.img backing-a.img" # Two different failures, and the order matters: absolutize() cds to the backing # file's DIRECTORY before write_cow_header ever stats the file, so a missing # directory and a missing file inside a real one take different paths. check_out uml-utilities/mkcow-no-dir "absolutize : Can't cd to" \ sh -c "cd '$t' && uml_mkcow cow-missing.img /nonexistent/backing.img 2>&1 || true" check_out uml-utilities/mkcow-no-file "Stat of backing file" \ sh -c "cd '$t' && uml_mkcow cow-missing2.img '$t/absent.img' 2>&1 || true" # A file whose magic is not MOOO and whose first words are not a V1/V2 header. dd if=/dev/urandom of="$t/junk.cow" bs=512 count=16 status=none check_out uml-utilities/moo-bad-magic "Reading COW header failed" \ sh -c "cd '$t' && uml_moo junk.cow out.img 2>&1 || true" # ------------------------------------- the two the ticket names as expected reds -- # tunctl needs a tun clone device. It is absent on this image, and unlike the # uml_mconsole case below that is a platform gap rather than a property of the # tool: a kernel with the tun driver would make this pass unchanged. if [ -c /dev/net/tun ] || [ -c /dev/misc/net/tun ]; then check uml-utilities/tunctl-create sh -c "/usr/bin/tunctl.uml-utilities -b -t bst-tap0 >/dev/null 2>&1" else # Not a red, and not this port's finding to re-derive: 63-tunctl.test already # establishes with a control -- opening a hand-made tun node and reading the # errno -- that this kernel has no tun DRIVER (CONFIG_TUN unset) rather than a # missing device node. TUNSETIFF cannot succeed under that, for any tunctl. bst_skip uml-utilities/tunctl-create \ "no TUN/TAP driver on this kernel (CONFIG_TUN unset, established by 63-tunctl.test's hand-made-node control), so no clone device exists and TUNSETIFF cannot succeed" fi # What IS reachable without a driver: the compiled-in default clone device is # named in the help text, and the open(2) failure is reported rather than # swallowed. Both are correct behaviour and are asserted as passes. check_out uml-utilities/tunctl-default-clone "The default tun clone device is /dev/net/tun" \ sh -c "/usr/bin/tunctl.uml-utilities -h 2>&1 || true" check_out uml-utilities/tunctl-open-diag "/dev/net/tun" \ sh -c "/usr/bin/tunctl.uml-utilities -t bst-tap0 2>&1 || true" # uml_mconsole talks to the mconsole socket a running UML instance creates. No # such instance can exist here -- UML is a host-kernel port for x86/arm and this # image is a wasm32 guest -- so the capability is skipped rather than failed: # a `not ok` is for a tool that is broken, and nothing about this one is. bst_skip uml-utilities/mconsole-live \ "needs a running User-Mode-Linux instance to own the mconsole socket; UML is a host-kernel port and cannot run on this image" # What IS reachable: the socket path is opened and the failure reported. check_out uml-utilities/mconsole-no-socket "couldn't stat file" \ sh -c "uml_mconsole /nonexistent/mconsole version 2>&1 || true" # ---------------------------------------------------------------- the rest ---- # uml_switch and uml_watchdog are daemons that need a peer; jailtest probes the # host kernel's jail support. Each is driven only as far as it goes without one. check_out uml-utilities/switch-bad-opt "Usage :" sh -c "uml_switch -no-such-option 2>&1 || true" check uml-utilities/jailtest-runs sh -c "jailtest >/dev/null 2>&1" rm -rf "$t"