. "$BST_LIB" # mtools 4.0.49, part 2 of 3 — the CODEPAGE contract this port turns on, mformat's # geometry, and the device-level tools (mcat, mbadblocks, mpartition, mzip, # mkmanifest) plus an independent fsck.fat check of what mformat wrote. # # Part 1 (65-mtools.test) covers the payload and option surface, part 3 # (65-mtools-files.test) the file operations. All three emit mtools/* ids. # # Three measured findings drive this file: # * an unknown codepage is "Bad codepage ", NOT an iconv error — that string # is the whole point of the port (below). # * mformat -r counts the root directory in SECTORS where dosfstools' mkfs.fat # -r counts ENTRIES. Both tools are on this image and the flag letter is the # same, which is exactly how that gets misread. # * FAT32's filesystem-type string lives at offset 82 and offset 54 is left # ZEROED, so reading the FAT12/16 offset on a FAT32 volume finds nothing. require mformat mtools/fmt-present LC_ALL=C; export LC_ALL T=${TMPDIR:-/tmp}/bst-mtoolsg.$$ rm -rf "$T"; mkdir -p "$T" || { bst_notok mtools/fmt-tmpdir "cannot create $T"; exit 0; } rdb() { dd if="$1" bs=1 skip="$2" count="$3" 2>/dev/null; } rdx() { dd if="$1" bs=1 skip="$2" count="$3" 2>/dev/null | od -An -tx1 | tr -d ' \n'; } mk() { dd if=/dev/zero of="$1" bs=1024 count="$2" >/dev/null 2>&1; } mig() { minfo -i "$1" :: 2>&1 | sed -n "s/^$2: *\([0-9][0-9]*\).*/\1/p" | head -1; } check_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 } # format $2 through a drive whose codepage is $1 — the only channel mtools has # for a codepage, since mformat carries no flag for it. cpfmt() { printf 'drive z: file="%s" codepage=%s\n' "$2" "$1" > "$T/cp.conf" MTOOLSRC="$T/cp.conf" mformat z: } # ------------------------------------------------------- A. the codepage path -- # mtools converts between the DOS codepage and wchar_t for every name and label, # and chooses the mechanism at BUILD time: with it calls # iconv_open("CP"), without it it uses the tables in codepages.c. glibc serves # CP850 from a dlopen'd gconv module this arm has not got, so the iconv build is # INERT here — measured on target with the stock recipe: mformat exits 1 with # "Error converting to codepage 850 Invalid argument" and every later tool then # reports "non DOS media". # # These three cases are the port's regression guard: "Bad codepage " is emitted # ONLY by the bundled-table build. If the bbappend's ac_cv_header_iconv_h=no is # ever lost, mtools still builds, installs and reports the right version, and only # these go red. mk "$T/cp.img" 1440 check_rc "mtools/cp-unknown-rc" 1 cpfmt 1252 "$T/cp.img" check_msg "mtools/cp-unknown-msg" "Bad codepage 1252" cpfmt 1252 "$T/cp.img" check_msg "mtools/cp-not-iconv" "Bad codepage 1252" cpfmt 1252 "$T/cp.img" # the tables that ARE bundled: 437/819/850/852/860/863/865/950, default 850 for cp in 437 819 850 852 860 863 865 950; do check_rc "mtools/cp-$cp" 0 cpfmt "$cp" "$T/cp.img" done # 932 (Shift-JIS) is a real DOS codepage that is NOT in the bundled set — the # bound of what this build can do, stated rather than discovered later. check_rc "mtools/cp-932-absent" 1 cpfmt 932 "$T/cp.img" check_msg "mtools/cp-932-absent-msg" "Bad codepage 932" cpfmt 932 "$T/cp.img" rm -f "$T/cp.img" "$T/cp.conf" # The other half of the same story, and it is NOT mtools'. mtools renders every # character the C locale cannot encode as '_' — but HOW MANY it renders depends on # what argv actually delivered, and on this runtime argv is not 8-bit clean: the # shell holds 'a\351b' as 61 e9 62, while the same string reaching a program # through argv (or environ) arrives as 61 EF BF BD 62 — the lone byte replaced by # U+FFFD. Through a FILE it arrives intact. So mtools sees THREE unencodable # characters here where a native run sees one, and prints three underscores. # # The delivered length is therefore measured at runtime rather than hardcoded: # these cases assert mtools' own rule (one '_' per unencodable character), which # stays true whatever the platform does with argv, instead of freezing today's # substitution into a tripwire that would go red the day argv is made 8-bit clean. HI=$(printf 'caf\351') DELIV=$(sh -c 'printf "%s" "$1"' _ "$HI") NBAD=$(( $(printf '%s' "$DELIV" | wc -c) - 3 )) UND=; i=0 while [ "$i" -lt "$NBAD" ]; do UND="${UND}_"; i=$((i+1)); done # argv and environ agree with each other, and a file preserves the byte outright check_eq "mtools/argv-eq-environ" "$(printf '%s' "$DELIV" | od -An -tx1 | tr -d ' \n')" \ "$(HIV="$HI" sh -c 'printf "%s" "$HIV"' | od -An -tx1 | tr -d ' \n')" check_eq "mtools/file-is-8bit-clean" "636166e9" \ "$(printf 'caf\351' > "$T/hb"; od -An -tx1 < "$T/hb" | tr -d ' \n'; rm -f "$T/hb")" mk "$T/hi.img" 1440; mformat -i "$T/hi.img" :: >/dev/null 2>&1 check "mtools/hi-mmd" mmd -i "$T/hi.img" "::/$HI" check_eq "mtools/hi-mdir" "::/caf$UND/" \ "$(mdir -i "$T/hi.img" -b :: 2>/dev/null | grep caf)" check "mtools/hi-mlabel" mlabel -i "$T/hi.img" "::$HI" check_out "mtools/hi-label-read" "CAF$UND" mdir -i "$T/hi.img" :: rm -f "$T/hi.img" # ----------------------------------------------- B. mformat: create + geometry -- FD=$T/fd.img mk "$FD" 1440 check "mtools/mformat-1440" mformat -i "$FD" :: check_eq "mtools/boot-signature" "55aa" "$(rdx "$FD" 510 2)" check_eq "mtools/oem-banner" "MTOO4049" "$(rdb "$FD" 3 8)" check_eq "mtools/media-byte" "f0" "$(rdx "$FD" 21 1)" check_eq "mtools/fstype-fat12" "FAT12" "$(rdb "$FD" 54 8 | cut -c1-5)" # mformat does not resize: the file must already be the size you want check_eq "mtools/mformat-keeps-size" "1474560" "$(stat -c %s "$FD")" # minfo reads the geometry back out of the boot sector mformat just wrote check_eq "mtools/geom-sector" "512" "$(mig "$FD" 'sector size')" check_eq "mtools/geom-cluster" "1" "$(mig "$FD" 'cluster size')" check_eq "mtools/geom-reserved" "1" "$(mig "$FD" 'reserved (boot) sectors')" check_eq "mtools/geom-fats" "2" "$(mig "$FD" 'fats')" check_eq "mtools/geom-rootents" "224" "$(mig "$FD" 'max available root directory slots')" check_out "mtools/minfo-cmdline" 'mformat -t 80 -h 2 -s 18' minfo -i "$FD" :: # the geometry knobs reach the on-disk structures; -r is the trap (sectors, not # entries), so -r 32 yields 32*16 = 512 slots mk "$T/geo.img" 4096 mformat -c 4 -r 32 -R 4 -i "$T/geo.img" :: >/dev/null 2>&1 check_eq "mtools/knob-cluster" "4" "$(mig "$T/geo.img" 'cluster size')" check_eq "mtools/knob-r-is-sectors" "512" "$(mig "$T/geo.img" 'max available root directory slots')" check_eq "mtools/knob-reserved" "4" "$(mig "$T/geo.img" 'reserved (boot) sectors')" rm -f "$T/geo.img" # a label set at format time, and the serial line minfo/mdir always print mk "$T/lbl.img" 1440 mformat -v FMTLABEL -i "$T/lbl.img" :: >/dev/null 2>&1 check_out "mtools/mformat-v-label" "FMTLABEL" mdir -i "$T/lbl.img" :: check_out "mtools/serial-line" "Volume Serial Number" mdir -i "$T/lbl.img" :: rm -f "$T/lbl.img" # FAT width follows SIZE, and 12/16 are the only widths ever chosen by default — # a 16 MB image comes out FAT16, exactly as dosfstools' mkfs.fat does. mk "$T/s16.img" 16384 check "mtools/mformat-16m" mformat -i "$T/s16.img" :: check_eq "mtools/fstype-fat16" "FAT16" "$(rdb "$T/s16.img" 54 8 | cut -c1-5)" rm -f "$T/s16.img" # FAT32 must be ASKED for with -F, and is then accepted far below its cluster # minimum. Its type string is at offset 82 and 54 is left zeroed. mk "$T/f32.img" 8192 check "mtools/mformat-F32" mformat -F -i "$T/f32.img" :: check_eq "mtools/fstype-fat32" "FAT32" "$(rdb "$T/f32.img" 82 8 | cut -c1-5)" check_eq "mtools/fat32-54-empty" "" "$(rdb "$T/f32.img" 54 5 | tr -d '\0')" check_out "mtools/fat32-mdir" "bytes free" mdir -i "$T/f32.img" :: rm -f "$T/f32.img" # error paths check_rc "mtools/mformat-missing-rc" 1 mformat -i "$T/no/such.img" :: check_msg "mtools/mformat-missing-msg" "No such file or directory" mformat -i "$T/no/such.img" :: check_rc "mtools/mdir-missing-rc" 1 mdir -i "$T/nope.img" :: check_msg "mtools/mdir-missing-msg" "Cannot initialize '::'" mdir -i "$T/nope.img" :: # --------------------------------------------- C. tools that are not file ops -- # mcat dumps the raw device; the OEM banner mformat wrote is the cheapest proof it # read the right bytes without putting binary through a result line. check_rc "mtools/mcat-rc" 0 mcat -i "$FD" :: check_eq "mtools/mcat-banner" "MTOO4049" \ "$(mcat -i "$FD" :: 2>/dev/null | dd bs=1 skip=3 count=8 2>/dev/null)" # mbadblocks scans the free clusters and reports progress against the total check_rc "mtools/mbadblocks-rc" 0 mbadblocks -i "$FD" :: check_msg "mtools/mbadblocks-out" "/2847" mbadblocks -i "$FD" :: # an unpartitioned image is not a partition, and a Zip drive it is not either. # mzip's first line on target is an ioctl complaint the native reference does not # print (there is no SCSI layer here); the verdict below it is the assertion. check_rc "mtools/mpartition-rc" 1 mpartition -i "$FD" :: check_msg "mtools/mpartition-msg" "is not a partition" mpartition -i "$FD" :: check_rc "mtools/mzip-rc" 1 mzip -i "$FD" :: check_msg "mtools/mzip-msg" "is not a Zip or Jaz drive" mzip -i "$FD" :: # mkmanifest emits the mv commands that restore long names after an 8.3 transfer check_rc "mtools/mkmanifest-rc" 0 mkmanifest "a very long filename.txt" check_msg "mtools/mkmanifest-out" "mv " mkmanifest "a very long filename.txt" # mtools expands a device path containing shell metacharacters by fork()ing # /bin/sh -c "echo …" and reading the answer back through a pipe (expand.c). That # is a real fork+exec+pipe on a runtime where posix_spawn is ENOSYS for other # packages (BRINTOS-181), so it is worth a guard rather than an assumption: if it # ever stops working, mtools dies with "Pipe read error" on any glob-shaped path. check "mtools/expand-glob" mdir -i "$T/f?.img" :: check_rc "mtools/expand-nomatch-rc" 1 mdir -i "$T/zz*.img" :: # ------------------------------------------- D. an independent reader agrees -- # dosfstools ships on this image, so what mformat wrote can be checked by a # SEPARATE implementation — the strongest available statement that mtools laid # down a real FAT and not merely something it can read back itself. if command -v fsck.fat >/dev/null 2>&1; then check_rc "mtools/fsck-clean-rc" 0 fsck.fat -n "$FD" check_out "mtools/fsck-12bit" "12 bit entries" fsck.fat -n -v "$FD" check_out "mtools/fsck-oem" 'System ID "MTOO4049"' fsck.fat -n -v "$FD" check_out "mtools/fsck-clusters" "2847 data clusters" fsck.fat -n -v "$FD" else bst_skip mtools/fsck-clean-rc "dosfstools not on this image" fi rm -rf "$T"