. "$BST_LIB" # libuuid is util-linux's DCE/RFC-4122 UUID library, split out of util-linux into # its own recipe so packages that need only UUIDs (e2fsprogs, erofs-utils, parted) # do not drag the whole suite in -- and, upstream's stated reason, so util-linux # can optionally build-depend on cryptsetup without a dependency cycle. It is here # because the filesystem and disk-image tooling on this Epic DEPENDS on it. # # WHAT THIS SUITE ASSERTS # # Two halves, and the second is the one that matters. # # 1. The SHIPPED ARTIFACT -- header, pkg-config file and static archive -- in # the header-and-archive shape every library on this image uses. The # completeness check reads every function the INSTALLED header declares and # requires each one in the archive, so a partial compile, a lost object or a # header/library skew all fail it. Tracking the header rather than a list # written here is what makes it survive a version bump. # # 2. The LIBRARY ACTUALLY RUNNING, through uuidgen(1). This is the difference # from the artifact-only library suites before it (libpipeline, popt, # libyaml): those ship no program and nothing on the image links them, so # they can only assert file shape. libuuid has a consumer ON THIS IMAGE -- # util-linux builds uuidgen against it (util-linux_2.39.3.bb builds libuuid # for uuidgen, then deletes it and lets this recipe ship it) -- so every # generator below is libuuid executing on the wasm32-hwjs runtime, not a # file listing. A dependent that links libuuid.a gets code proven to run. # # WHY THE NAMESPACE CASES ARE THE STRONGEST ONES # # uuid_generate_md5()/uuid_generate_sha1() are pure functions of (namespace, name) # fixed by RFC 4122, so they have ONE correct answer that is independent of this # machine, this clock and this entropy source. Everything else libuuid produces is # random or time-dependent and can only be checked structurally -- a v4 generator # that returned a constant would still satisfy a format check. These two cases # compare against the published vectors, so they detect a *wrong* digest, not # merely a well-shaped one. They are the only cases here that can. # # The values are RFC 4122's DNS namespace (6ba7b810-9dad-11d1-80b4-00c04fd430c8) # with the name "example.com": # v3/MD5 9073926b-929f-31c2-abc9-fad77ae3e8eb # v5/SHA1 cfbff0d1-9375-5685-968c-48ce8b15ae17 # # --------------------------------------------------------------------------- # WHY THE SYMBOL CHECK USES strings(1) AND NOT nm # # There is no nm on this image (busybox provides strings, not binutils), so the # archive has to be read as bytes. The obvious byte reading -- split on NUL and # match whole tokens -- is WRONG HERE and was caught by this suite's own control # case: a wasm object stores its symbol names LENGTH-PREFIXED in the linking # section rather than NUL-terminated, so names run together and whether any given # one happens to be NUL-bracketed is luck. That method reported uuid_clear absent # from an archive whose la-clear.o demonstrably defines it (llvm-nm: T uuid_clear), # i.e. a false negative that looked like a real porting defect. # # strings(1) splits on the non-printable length byte instead, and the greedy # `uuid_[a-z0-9_]+` extraction then yields each identifier WHOLE -- so # uuid_generate_md5 comes out as itself and never as a match for uuid_generate. # The bogus-symbol control below keeps the whole check honest: if it ever passes, # the extraction is matching everything and every OK above it is vacuous. # --------------------------------------------------------------------------- # # POSIX sh only (busybox ash), and text assertions are `case` globs against # variables read once -- on this runtime the forking is the cost, not the work. inc=/usr/include/uuid/uuid.h pc=/usr/lib/pkgconfig/uuid.pc lib=/usr/lib/libuuid.a nl=' ' # hasfile -- ok iff is a non-empty regular file. # has -- ok iff contains the literal . # hasline -- ok iff carries as a WHOLE line. # Substring matching would let uuid_generate_time # satisfy a lookup for uuid_generate, and # uuid_unparse_lower satisfy uuid_unparse -- this # header is full of such prefixes and a substring # test would pass vacuously on them. hasfile() { if [ -s "$2" ]; then bst_ok "$1"; else bst_notok "$1" "absent or empty: $2"; fi; } has() { case "$3" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "not found: $2" ;; esac; } hasline() { case "$nl$3$nl" in *"$nl$2$nl"*) bst_ok "$1" ;; *) bst_notok "$1" "not in archive: $2" ;; esac; } # --- the header --------------------------------------------------------------- hasfile libuuid/header-present "$inc" h=$(cat "$inc" 2>/dev/null) has libuuid/header-guard '_UL_LIBUUID_UUID_H' "$h" has libuuid/type-uuid_t 'uuid_t' "$h" has libuuid/variant-dce 'UUID_VARIANT_DCE' "$h" has libuuid/type-v4 'UUID_TYPE_DCE_RANDOM' "$h" # --- the pkg-config file ------------------------------------------------------ # A consumer resolves -luuid through this file, so an absent or wrong Libs line # breaks e2fsprogs/erofs-utils at THEIR final link, not here. hasfile libuuid/pc-present "$pc" p=$(cat "$pc" 2>/dev/null) has libuuid/pc-name 'Name: uuid' "$p" has libuuid/pc-libs '-luuid' "$p" # --- the archive -------------------------------------------------------------- # This arm is static-only: no libuuid.so is built or shipped, and a .so appearing # here would mean the distro's --disable-shared stopped reaching this recipe. hasfile libuuid/archive-present "$lib" if [ -e /usr/lib/libuuid.so ] || [ -e /lib/libuuid.so ]; then bst_notok libuuid/no-shared "a libuuid.so is present on a static-only arm" else bst_ok libuuid/no-shared fi # --- completeness: every declared function is in the archive ------------------- if [ ! -s "$lib" ]; then bst_skip libuuid/decls-found "archive absent" elif ! command -v strings >/dev/null 2>&1; then bst_skip libuuid/decls-found "no strings(1) to read the archive" else syms=$(strings "$lib" 2>/dev/null | grep -oE 'uuid_[a-z0-9_]+' | sort -u) decls=$(sed 's,/\*.*\*/,,' "$inc" 2>/dev/null \ | awk 'match($0, /uuid_[a-z0-9_]*[ \t]*\(/) { s = substr($0, RSTART, RLENGTH - 1); gsub(/[ \t]/, "", s); print s }' | sort -u) n=0 for d in $decls; do n=$((n + 1)) hasline "libuuid/sym-$d" "$d" "$syms" done if [ "$n" -ge 15 ]; then bst_ok libuuid/decls-found else bst_notok libuuid/decls-found "only $n function declarations parsed from $inc"; fi # The control for every sym- case above. A symbol that cannot exist must NOT be # found; if it is, the extraction is matching indiscriminately and the whole # completeness check is meaningless rather than passing. if printf '%s\n' "$syms" | grep -qx 'uuid_definitely_not_a_symbol'; then bst_notok libuuid/sym-control "bogus symbol matched — the sym- checks are vacuous" else bst_ok libuuid/sym-control fi fi # --- the library running, through uuidgen(1) ---------------------------------- require uuidgen libuuid/uuidgen-present # is_uuid -- 0 iff the string is exactly 8-4-4-4-12 lowercase hex. # Written as a glob rather than a regex so it costs no fork. hx='[0-9a-f]' is_uuid() { case "$1" in $hx$hx$hx$hx$hx$hx$hx$hx-$hx$hx$hx$hx-$hx$hx$hx$hx-$hx$hx$hx$hx-$hx$hx$hx$hx$hx$hx$hx$hx$hx$hx$hx$hx) return 0 ;; *) return 1 ;; esac } # Default form: a v4 (random) UUID. u1=$(uuidgen 2>&1) if is_uuid "$u1"; then bst_ok libuuid/gen-format else bst_notok libuuid/gen-format "not a UUID: $(_bst_1line "$u1")"; fi # RFC 4122 fixes the version nibble (position 15) and the variant nibble # (position 20) -- a generator that emitted well-formed hex with the wrong bits # would pass the format check above and fail here. case "$u1" in ????????-????-4???-*) bst_ok libuuid/gen-version-4 ;; *) bst_notok libuuid/gen-version-4 "version nibble is not 4: $u1" ;; esac case "$u1" in ????????-????-????-[89ab]???-*) bst_ok libuuid/gen-variant-rfc4122 ;; *) bst_notok libuuid/gen-variant-rfc4122 "variant nibble not in 8-b: $u1" ;; esac # Two calls must differ. This is the case that would catch a libuuid whose # entropy source is stubbed on this runtime -- a constant UUID is well-formed, # correctly versioned, and useless. u2=$(uuidgen 2>&1) if [ "$u1" != "$u2" ] && is_uuid "$u2"; then bst_ok libuuid/gen-unique else bst_notok libuuid/gen-unique "two generations did not differ: $u1 $u2"; fi # -r is the explicit spelling of the default. ur=$(uuidgen -r 2>&1) if is_uuid "$ur"; then bst_ok libuuid/gen-random else bst_notok libuuid/gen-random "uuidgen -r: $(_bst_1line "$ur")"; fi # The two deterministic generators. See the header comment: these are the only # cases with a machine-independent right answer. um=$(uuidgen -m -n @dns -N example.com 2>&1) check_eq libuuid/md5-dns-example 9073926b-929f-31c2-abc9-fad77ae3e8eb "$um" us=$(uuidgen -s -n @dns -N example.com 2>&1) check_eq libuuid/sha1-dns-example cfbff0d1-9375-5685-968c-48ce8b15ae17 "$us" # Same input, same output -- the property a content-addressed consumer relies on. us2=$(uuidgen -s -n @dns -N example.com 2>&1) check_eq libuuid/sha1-deterministic "$us" "$us2" # A different name must give a different UUID (the digest is over the name, not # just the namespace). us3=$(uuidgen -s -n @dns -N example.org 2>&1) if [ "$us" != "$us3" ] && is_uuid "$us3"; then bst_ok libuuid/sha1-name-sensitive else bst_notok libuuid/sha1-name-sensitive "example.com and example.org agreed: $us3"; fi # -t is time-based (v1). It needs a clock-sequence state file and, classically, a # MAC address; libuuid falls back to a random node ID when there is no usable # interface. Reported rather than assumed: if this runtime cannot serve it, the # case says so against the observation instead of being omitted. ut=$(uuidgen -t 2>&1); trc=$? if [ "$trc" -eq 0 ] && is_uuid "$ut"; then case "$ut" in ????????-????-1???-*) bst_ok libuuid/gen-time-v1 ;; *) bst_notok libuuid/gen-time-v1 "version nibble is not 1: $ut" ;; esac else bst_skip libuuid/gen-time-v1 "uuidgen -t rc=$trc: $(_bst_1line "$ut")" fi