diff --git a/check b/check new file mode 100755 index 0000000..1696329 --- /dev/null +++ b/check @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +# Canonical verification for the CUBELinux-2 workspace. +# +# ./check full gate (fmt, tests, clippy -D warnings) +# ./check quick tests only +# ./check mount full gate + live FUSE mount end-to-end (needs root) +# +# Exit 0 means the tree on disk is green. This is the single source of truth; +# do not claim verification from an ad-hoc run. +# +# Why `mount` is opt-in: it needs root, /dev/fuse, and the `attr` package, and +# it is the only stage that exercises the real kernel VFS path. Two Package 3 +# defects (mkdir -p to depth 4, cross-user ACLs) passed every unit test and +# were caught only here — so run it before calling filesystem work done. +set -euo pipefail +cd "$(dirname "$0")" + +command -v cargo >/dev/null || PATH="$HOME/.cargo/bin:$PATH" +command -v cargo >/dev/null || { echo "cargo not found; add ~/.cargo/bin to PATH" >&2; exit 127; } + +FEAT="--features cubefs/mount" # the FUSE adapter must compile in every gate +JOBS="${JOBS:-4}" # never saturate all 8 cores by default + +step() { printf '\n\033[1m### %s\033[0m\n' "$1"; } + +if [ "${1:-}" = quick ]; then + step "tests" + exec cargo test --workspace $FEAT --jobs "$JOBS" +fi + +step "1/3 format" +cargo fmt --all -- --check + +step "2/3 tests" +cargo test --workspace $FEAT --jobs "$JOBS" + +step "3/3 clippy" +cargo clippy --workspace --all-targets $FEAT --jobs "$JOBS" -- -D warnings + +[ "${1:-}" = mount ] || { printf '\n\033[32mALL CHECKS PASSED\033[0m (run ./check mount for live FUSE e2e)\n'; exit 0; } + +step "4/4 live FUSE mount" +[ "$(id -u)" -eq 0 ] || { echo "SKIP: live mount needs root" >&2; exit 1; } +command -v getfattr >/dev/null || { echo "missing getfattr: apt-get install attr" >&2; exit 1; } + +cargo build $FEAT --jobs "$JOBS" +BIN=$PWD/target/debug/cubefs-mount +MNT=$(mktemp -d /tmp/cubefs-check.XXXXXX) +PASS=0; FAIL=0 +cleanup() { fusermount3 -u "$MNT" 2>/dev/null || true; sleep 0.5; rmdir "$MNT" 2>/dev/null || true; } +trap cleanup EXIT + +ok() { printf 'PASS %s\n' "$1"; PASS=$((PASS+1)); } +no() { printf '\033[31mFAIL %s -- %s\033[0m\n' "$1" "${2-}"; FAIL=$((FAIL+1)); } +is() { [ "$2" = "$3" ] && ok "$1" || no "$1" "want[$3] got[$2]"; } +# A command that must fail (permission, ENOTEMPTY, EINVAL...). +nay() { local m=$1; shift; "$@" >/dev/null 2>&1 && no "$m" "unexpectedly succeeded" || ok "$m"; } + +set +e +"$BIN" "$MNT" --seed --label check --allow-other >/dev/null 2>&1 & +sleep 2 +mount | grep -q "on $MNT type fuse" && ok mounted || { no mounted; exit 1; } + +F=$MNT/c001/z001/y001/x001 +is "seed read" "$(cat $F)" "hello from the cube" +is "root listing" "$(ls $MNT | tr '\n' ' ')" "c001 c002 " +is "stat size" "$(stat -c %s $F)" "20" +is "partial read" "$(dd if=$F bs=1 skip=6 count=4 2>/dev/null)" "from" + +echo posix-write > $MNT/c001/z001/y001/x003 +is "write+read" "$(cat $MNT/c001/z001/y001/x003)" "posix-write" +truncate -s 5 $MNT/c001/z001/y001/x003 +is "truncate" "$(cat $MNT/c001/z001/y001/x003)" "posix" +printf XY >> $MNT/c001/z001/y001/x003 +is "append" "$(cat $MNT/c001/z001/y001/x003)" "posixXY" +rm $MNT/c001/z001/y001/x003 +is "unlink" "$(ls $MNT/c001/z001/y001 | tr '\n' ' ')" "x001 x002 " + +# Regression: empty directories must be representable, or mkdir -p can never +# reach depth 4 (the kernel's revalidating lookup returned ENOENT). +mkdir -p $MNT/c050/z001/y001 && ok "mkdir -p depth3" || no "mkdir -p depth3" +is "empty dir listed" "$(ls $MNT | grep -c c050)" "1" +is "empty dir empty" "$(ls $MNT/c050/z001/y001 | wc -l)" "0" +echo deep > $MNT/c050/z001/y001/x001 +is "create under new" "$(cat $MNT/c050/z001/y001/x001)" "deep" +nay "rmdir nonempty refused" rmdir $MNT/c050 +mkdir $MNT/c060 && rmdir $MNT/c060 +is "mkdir/rmdir empty" "$(ls $MNT | grep -c c060)" "0" + +mkdir -p $MNT/c255/z255/y255 && echo edge > $MNT/c255/z255/y255/x255 +is "axis-255 corner" "$(cat $MNT/c255/z255/y255/x255)" "edge" +nay "reject non-canonical x1" tee $MNT/c001/z001/y001/x1 <</dev/null && ok "cp byte-identical" || no "cp byte-identical" + +setfattr -n user.a -v 1 $F; setfattr -n user.b -v 2 $F; setfattr -n user.d -v D $MNT/c001 +is "xattr count file" "$(getfattr -d $F 2>/dev/null | grep -c ^user)" "3" +is "xattr on dir" "$(getfattr -n user.d --only-values $MNT/c001 2>/dev/null)" "D" +setfattr -x user.a $F +is "xattr removed" "$(getfattr -d $F 2>/dev/null | grep -c '^user\.a')" "0" + +# Regression: cross-user ACLs need --allow-other, else the kernel returns +# EACCES at the mountpoint before any request reaches us. +if id luulu >/dev/null 2>&1; then + chown 0:0 $F; chmod 644 $F + is "644 root, other reads" "$(sudo -u luulu cat $F)" "hello from the cube" + chmod 600 $F + nay "600 root denies other" sudo -u luulu cat $F + chown 1000:1000 $F; chmod 600 $F + is "600 owned, owner reads" "$(sudo -u luulu cat $F)" "hello from the cube" + sudo -u luulu tee $F >/dev/null << $MNT/c100/z001/y001/x$(printf %03d $i); done +is "200 records" "$(ls $MNT/c100/z001/y001 | wc -l)" "200" +is "record 137" "$(cat $MNT/c100/z001/y001/x137)" "rec137" +set -e + +printf '\n%d passed, %d failed\n' "$PASS" "$FAIL" +[ "$FAIL" -eq 0 ] || exit 1 +printf '\n\033[32mALL CHECKS PASSED\033[0m\n' diff --git a/cubecoords/src/lib.rs b/cubecoords/src/lib.rs index 5703acf..1220ce7 100644 --- a/cubecoords/src/lib.rs +++ b/cubecoords/src/lib.rs @@ -155,9 +155,8 @@ impl TriEnc { let v = word.0; let control = ((v >> 60) & 0x0F) as u8; let mut ascii = [0u8; 6]; - for i in 0..6 { - let shift = 8 * (5 - i); - ascii[i] = ((v >> shift) & 0xFF) as u8; + for (i, b) in ascii.iter_mut().enumerate() { + *b = ((v >> (8 * (5 - i))) & 0xFF) as u8; } (control, ascii) } @@ -186,8 +185,8 @@ impl HeaderFlags { pub const PERM_REMOTE_USER: u16 = 1 << 6; /// Flag 8: has outgoing association links. pub const HAS_ASSOCIATIONS: u16 = 1 << 7; - /// Flag 255 (conceptual end-of-header) is represented out-of-band by the - /// record serializer; there is no bit for it. + // Flag 255 (conceptual end-of-header) is represented out-of-band by the + // record serializer; there is no bit for it. /// Construct from a raw bitmask. #[inline]