From 901e9792278f2e944b38a0013518b318285e3f5a Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 28 Jan 2026 15:44:47 +0100 Subject: [PATCH 01/50] test_kpartx: add cleanup action before running kpartx If kpartx crashes and the cleanup action isn't yet pushed, the cleanup will fail. Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index d3c9aef51..065d60a52 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -181,9 +181,9 @@ mk_partitions $LO4 # Test invocation of kpartx with regular file here LO2P1=/dev/mapper/$(basename $LO2)-foo1 +push_cleanup 'dmsetup remove -f $(basename $LO2P1)' $KPARTX $KPARTX_OPTS -a -p -foo $FILE2 [[ -b $LO2P1 ]] -push_cleanup 'dmsetup remove -f $(basename $LO2P1)' step "remove partitions with deleted ptable" wipe_ptable $LO2 @@ -195,8 +195,8 @@ $KPARTX $KPARTX_OPTS -a -p -foo $FILE2 [[ -b $LO2P1 ]] LO1P1=/dev/mapper/$(basename $LO1)-eggs1 -$KPARTX $KPARTX_OPTS -a -p -eggs $LO1 push_cleanup 'dmsetup remove -f $(basename $LO1P1)' +$KPARTX $KPARTX_OPTS -a -p -eggs $LO1 usleep $WAIT_US [[ -b $LO1P1 ]] From f733bed7c61e44e8604101e1ca2ebf91e315911f Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 28 Jan 2026 15:46:27 +0100 Subject: [PATCH 02/50] test_kpartx: Use system kpartx for cleanup If our kpartx binary is broken, it's better to use the system kpartx for cleanup, if possible. Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index 065d60a52..9dbc1ab83 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -83,6 +83,12 @@ step preparation fi } [[ $KPARTX ]] +echo "== Using kpartx = $KPARTX ==" >&2 + +# Try to use system kpartx for cleanup +# Fall back to ours if not found +CLEANUP_KPARTX=$(which kpartx) +[[ "$CLEANUP_KPARTX" ]] || CLEANUP_KPARTX=$KPARTX FILE1=kpartx1 FILE2=kpartx2 @@ -221,13 +227,13 @@ SPAN2P1=/dev/mapper/${SPAN2}-bar1 # udev rules may have created partition mappings without UUIDs # which aren't removed by default (if system standard kpartx doesn't # set the UUID). Remove them using -f -push_cleanup '$KPARTX $KPARTX_OPTS -f -d /dev/mapper/$SPAN2' +push_cleanup '$CLEANUP_KPARTX $KPARTX_OPTS -f -d /dev/mapper/$SPAN2' push_cleanup 'dmsetup remove -f $(basename $SPAN2P1)' $KPARTX $KPARTX_OPTS -a -p -spam /dev/mapper/$SPAN1 SPAN1P1=/dev/mapper/${SPAN1}-spam1 # see above -push_cleanup '$KPARTX $KPARTX_OPTS -f -d /dev/mapper/$SPAN1' +push_cleanup '$CLEANUP_KPARTX $KPARTX_OPTS -f -d /dev/mapper/$SPAN1' push_cleanup 'dmsetup remove -f $(basename $SPAN1P1)' usleep $WAIT_US From 3cecba948c1f8dbb6ecd9f447350881d20982dfa Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 28 Jan 2026 15:50:29 +0100 Subject: [PATCH 03/50] test_kpartx: hide dd output Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index 9dbc1ab83..836b06bea 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -69,7 +69,7 @@ mk_partitions() { } wipe_ptable() { - dd if=/dev/zero of=$1 bs=1b count=1 + dd if=/dev/zero of=$1 bs=1b count=1 &>/dev/null } step preparation From 01abd65f274993aa87018343c6df75095fa7f710 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 28 Jan 2026 15:53:01 +0100 Subject: [PATCH 04/50] test_kpartx: add function to debug current system state If something goes wrong, it's useful to see the current loop device and dm device setup on the system. Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index 836b06bea..6441001e4 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -29,6 +29,8 @@ : ${KPARTX:=} # Options to pass to kpartx always : ${KPARTX_OPTS:=-s} +# Set non-empty to enable some debug messages +: ${KPARTX_DEBUG:=} # Time to wait for device nodes to appear (microseconds) # Waiting is only needed if "s" is not in $KPARTX_OPTS : ${WAIT_US:=0} @@ -72,6 +74,12 @@ wipe_ptable() { dd if=/dev/zero of=$1 bs=1b count=1 &>/dev/null } +current_state() { + [[ "$KPARTX_DEBUG" ]] || return 0 + echo "--------------------" + dmsetup ls +} + step preparation [[ $UID -eq 0 ]] @@ -174,6 +182,7 @@ lvcreate --config "$LVMCONF" -L $((SIZE/2))B -n $LV $VG push_cleanup 'lvremove --config "$LVMCONF" -f $VG/$LV' usleep $WAIT_US +current_state [[ -b /dev/mapper/$VG-$LV ]] # dmsetup table /dev/mapper/$VG-$LV @@ -185,37 +194,49 @@ mk_partitions $LO1 mk_partitions $LO2 mk_partitions $LO4 +step "run kpartx on regular file" + # Test invocation of kpartx with regular file here LO2P1=/dev/mapper/$(basename $LO2)-foo1 push_cleanup 'dmsetup remove -f $(basename $LO2P1)' $KPARTX $KPARTX_OPTS -a -p -foo $FILE2 +current_state [[ -b $LO2P1 ]] step "remove partitions with deleted ptable" wipe_ptable $LO2 $KPARTX $KPARTX_OPTS -d $LO2 +current_state [[ ! -b $LO2P1 ]] +step "re-add just removed partions" mk_partitions $LO2 $KPARTX $KPARTX_OPTS -a -p -foo $FILE2 +current_state [[ -b $LO2P1 ]] +step "run kpartx on loop device" LO1P1=/dev/mapper/$(basename $LO1)-eggs1 push_cleanup 'dmsetup remove -f $(basename $LO1P1)' $KPARTX $KPARTX_OPTS -a -p -eggs $LO1 usleep $WAIT_US +current_state [[ -b $LO1P1 ]] [[ -b $LO2P1 ]] # dmsetup info $LO2P1 +step "rename $(basename $LO1P1) to $(basename $LO1)" # Set pathological name for partition on $LO1 (same as loop device itself) dmsetup rename $(basename $LO1P1) $(basename $LO1) LO1P1=/dev/mapper/$(basename $LO1) pop_cleanup push_cleanup 'dmsetup remove -f $(basename $LO1P1)' +current_state +[[ -b $LO1P1 ]] + # dmsetup info $LO1P1 step "create partitions on DM devices" @@ -237,17 +258,20 @@ push_cleanup '$CLEANUP_KPARTX $KPARTX_OPTS -f -d /dev/mapper/$SPAN1' push_cleanup 'dmsetup remove -f $(basename $SPAN1P1)' usleep $WAIT_US +current_state [[ -b $SPAN2P1 ]] [[ -b $SPAN1P1 ]] step "rename partitions on DM device to default" $KPARTX $KPARTX_OPTS -u /dev/mapper/$SPAN1 +current_state [[ ! -b ${SPAN1P1} ]] # This assumes that $SPAN1 ends in a non-digit [[ -b ${SPAN1P1//-spam/} ]] step "rename partitions on DM device back from default" $KPARTX $KPARTX_OPTS -u -p -spam /dev/mapper/$SPAN1 +current_state [[ -b ${SPAN1P1} ]] [[ ! -b ${SPANP1//-foo/} ]] @@ -255,6 +279,7 @@ step "delete partitions on DM devices" $KPARTX $KPARTX_OPTS -d /dev/mapper/$SPAN1 >&2 usleep $WAIT_US +current_state [[ -b $SPAN2P1 ]] [[ -b $LO1P1 ]] [[ -b $LO2P1 ]] @@ -263,29 +288,34 @@ usleep $WAIT_US $KPARTX $KPARTX_OPTS -d /dev/mapper/$SPAN2 usleep $WAIT_US +current_state [[ -b $LO1P1 ]] [[ -b $LO2P1 ]] [[ ! -b $SPAN2P1 ]] step "rename partitions on loop device" $KPARTX $KPARTX_OPTS -u -p -spam $LO2 +current_state [[ ! -b ${LO2P1} ]] [[ -b ${LO2P1//-foo/-spam} ]] step "rename partitions on loop device back" $KPARTX $KPARTX_OPTS -u -p -foo $LO2 +current_state [[ -b ${LO2P1} ]] [[ ! -b ${LO2P1//-foo/-spam} ]] step "rename partitions on loop device to default" $KPARTX $KPARTX_OPTS -u $LO2 #read a +current_state [[ ! -b ${LO2P1} ]] # $LO1 ends in a digit [[ -b ${LO2P1//-foo/p} ]] step "rename partitions on loop device back from default" $KPARTX $KPARTX_OPTS -u -p -foo $LO2 +current_state [[ -b ${LO2P1} ]] [[ ! -b ${LO2P1//-foo/p} ]] @@ -302,11 +332,15 @@ $KPARTX $KPARTX_OPTS -d $LO1 usleep $WAIT_US # ls -l /dev/mapper +current_state [[ ! -b $LO1P1 ]] +# 'dmsetup remove -f $(basename $SPAN1P1)' pop_cleanup [[ ! -b $LO2P1 ]] +# '$CLEANUP_KPARTX $KPARTX_OPTS -f -d /dev/mapper/$SPAN1' pop_cleanup # spans should not have been removed +current_state [[ -b /dev/mapper/$SPAN1 ]] [[ -b /dev/mapper/$SPAN2 ]] [[ -b /dev/mapper/$USER1 ]] From aa0d266e6c397aecc052e634e46c3b0b371f94eb Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 28 Jan 2026 15:54:06 +0100 Subject: [PATCH 05/50] test_kpartx: run cleanly on systems using lvmdevices Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index 6441001e4..159241e11 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -107,7 +107,6 @@ SECTSIZ=512 OFFS=32 # offset of linear mapping into dev, sectors VG=kpvg # volume group name LV=kplv # logical vol name -LVMCONF='devices { filter = [ "a|/dev/loop.*|", r".*" ] }' OK= @@ -116,6 +115,21 @@ OK= push_cleanup 'rm -rf $WORKDIR' } +USE_DEVICESFILE= +if [[ "$((lvmconfig --typeconfig full 2>/dev/null || true) | \ + sed -n 's/.*use_devicesfile=//p')" = 1 ]]; then + LVMCONF= + # This test may modify the devices file. Be sure to restore it + if [[ -f /etc/lvm/devices/system.devices ]]; then + cp -a /etc/lvm/devices/system.devices "$WORKDIR" + push_cleanup 'cp -a $WORKDIR/system.devices /etc/lvm/devices' + else + push_cleanup 'rm -f /etc/lvm/devices/system.devices' + fi +else + LVMCONF='devices { filter = [ "a|/dev/loop.*|", r".*" ] }' +fi + push_cleanup "cd $PWD" cd "$WORKDIR" @@ -175,11 +189,11 @@ usleep $WAIT_US step "create vg on $LO3" # On the 3rd loop device, we create a VG and an LV # The LV should not be removed by kpartx. -pvcreate --config "$LVMCONF" -f $LO3 -vgcreate --config "$LVMCONF" $VG $LO3 -push_cleanup 'vgremove --config "$LVMCONF" -f $VG' -lvcreate --config "$LVMCONF" -L $((SIZE/2))B -n $LV $VG -push_cleanup 'lvremove --config "$LVMCONF" -f $VG/$LV' +pvcreate ${LVMCONF:+--config "$LVMCONF"} -f $LO3 +vgcreate ${LVMCONF:+--config "$LVMCONF"} $VG $LO3 +push_cleanup 'vgremove ${LVMCONF:+--config "$LVMCONF"} -f $VG' +lvcreate ${LVMCONF:+--config "$LVMCONF"} -L $((SIZE/2))B -n $LV $VG +push_cleanup 'lvremove ${LVMCONF:+--config "$LVMCONF"} -f $VG/$LV' usleep $WAIT_US current_state From a76724d77e1d732724c2e6efa79dec446ca7ba87 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 28 Jan 2026 15:56:07 +0100 Subject: [PATCH 06/50] test-kpartx: use more unusual VG and LV names Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index 159241e11..902c764ef 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -105,8 +105,9 @@ FILE4=kpartx4 SIZE=$((1024*1024*1024)) # use bytes as units here SECTSIZ=512 OFFS=32 # offset of linear mapping into dev, sectors -VG=kpvg # volume group name -LV=kplv # logical vol name +VG=__kpvg__ # volume group name +LV=__kplv__ # logical vol name + OK= @@ -166,10 +167,11 @@ TABLE="\ 0 $((SIZE/SECTSIZ-OFFS)) linear $DEV1 $OFFS $((SIZE/SECTSIZ-OFFS)) $((SIZE/SECTSIZ-OFFS)) linear $DEV2 $OFFS" -SPAN1=kpt SPAN2=$(basename $LO2) dmsetup create $SPAN1 <<<"$TABLE" push_cleanup 'dmsetup remove -f $SPAN1' +# This must end in a normal letter +SPAN1=__kpt__ dmsetup create $SPAN2 <<<"$TABLE" push_cleanup 'dmsetup remove -f $SPAN2' From 1d2135a9525cdc57d928fca8c6138d423afdb9be Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 28 Jan 2026 16:12:28 +0100 Subject: [PATCH 07/50] test-kpartx: improve output for errors and cleanup Make this output better readable and understandable. Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index 902c764ef..47cd15b8e 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -36,7 +36,10 @@ : ${WAIT_US:=0} # IMPORTANT: The ERR trap is essential for this program to work correctly! -trap 'LINE=$LINENO; trap - ERR; echo "== error in $BASH_COMMAND on line $LINE ==" >&2; exit 1' ERR +trap 'LINE=$LINENO +trap - ERR +eval echo "$0: == error in \\\"\$BASH_COMMAND\\\" \(\\\"$BASH_COMMAND\\\"\) on line $LINE ==" >&2 +exit 1' ERR trap 'cleanup' 0 CLEANUP=: @@ -48,11 +51,17 @@ cleanup() { else echo == step $STEP failed == >&2 fi + if [[ "$KPARTX_DEBUG" ]]; then + eval "echo \"== BEGIN CLEANUP == +$CLEANUP +== END CLEANUP ==\"" + fi eval "$CLEANUP" &>/dev/null } push_cleanup() { - CLEANUP="$@;$CLEANUP" + CLEANUP="$@ +$CLEANUP" } pop_cleanup() { From 747add14ccc605ff41f85b3fbea065d028a93eab Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 28 Jan 2026 16:18:26 +0100 Subject: [PATCH 08/50] test-kpartx: support running from kpartx subdirectory If run directly from the kpartx subdirectory, execute the local kpartx binary. Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index 47cd15b8e..29888c8bc 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -93,7 +93,9 @@ step preparation [[ $UID -eq 0 ]] [[ $KPARTX ]] || { - if [[ -x $PWD/kpartx/kpartx ]]; then + if [[ -f "$PWD/kpartx" && -x "$PWD/kpartx" ]]; then + KPARTX=$PWD/kpartx + elif [[ -f "$PWD/kpartx/kpartx" && -x "$PWD/kpartx/kpartx" ]]; then KPARTX=$PWD/kpartx/kpartx else KPARTX=$(which kpartx) From a73dbf0588d477bdd9d85a3f280dc6ec32e6d0d1 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 28 Jan 2026 18:40:56 +0100 Subject: [PATCH 09/50] test-kpartx: fix shellcheck-reported problems Mostly missing quotes. SC2016 must be generally disabled because it will complain about every push_cleanup invocation. SC2086 produces false positives for KPARTX_OPTS. Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 334 ++++++++++++++++++++++++--------------------- 1 file changed, 181 insertions(+), 153 deletions(-) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index 29888c8bc..5bce02b85 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -1,5 +1,8 @@ #! /bin/bash +# Run shellcheck with "-e SC2016", this is causing too many annotations +# (for each push_cleanup invocation). + # This is a unit test program for kpartx, in particular for deleting partitions. # # The rationale is the following: @@ -24,16 +27,16 @@ # Set WORKDIR in environment to existing dir to for persistence # WARNING: existing files will be truncated. # If empty, test will be done in temporary dir -: ${WORKDIR:=} +: "${WORKDIR:=}" # Set this environment variable to test an alternative kpartx executable -: ${KPARTX:=} +: "${KPARTX:=}" # Options to pass to kpartx always -: ${KPARTX_OPTS:=-s} +: "${KPARTX_OPTS:=-s}" # Set non-empty to enable some debug messages -: ${KPARTX_DEBUG:=} +: "${KPARTX_DEBUG:=}" # Time to wait for device nodes to appear (microseconds) # Waiting is only needed if "s" is not in $KPARTX_OPTS -: ${WAIT_US:=0} +: "${WAIT_US:=0}" # IMPORTANT: The ERR trap is essential for this program to work correctly! trap 'LINE=$LINENO @@ -46,10 +49,10 @@ CLEANUP=: cleanup() { trap - ERR trap - 0 - if [[ $OK ]]; then - echo == all tests completed successfully == >&2 + if [[ "$OK" ]]; then + echo "== all tests completed successfully ==" >&2 else - echo == step $STEP failed == >&2 + echo "== step $STEP failed ==" >&2 fi if [[ "$KPARTX_DEBUG" ]]; then eval "echo \"== BEGIN CLEANUP == @@ -60,7 +63,7 @@ $CLEANUP } push_cleanup() { - CLEANUP="$@ + CLEANUP="$* $CLEANUP" } @@ -70,17 +73,17 @@ pop_cleanup() { } step() { - STEP="$@" - echo == Test step: $STEP == >&2 + STEP=$* + echo "== Test step: $STEP ==" >&2 } mk_partitions() { - parted -s $1 mklabel msdos - parted -s -- $1 mkpart prim ext2 1MiB -1s + parted -s "$1" mklabel msdos + parted -s -- "$1" mkpart prim ext2 1MiB -1s } wipe_ptable() { - dd if=/dev/zero of=$1 bs=1b count=1 &>/dev/null + dd if=/dev/zero of="$1" bs=1b count=1 &>/dev/null } current_state() { @@ -91,8 +94,8 @@ current_state() { step preparation -[[ $UID -eq 0 ]] -[[ $KPARTX ]] || { +[[ "$UID" -eq 0 ]] +[[ "$KPARTX" ]] || { if [[ -f "$PWD/kpartx" && -x "$PWD/kpartx" ]]; then KPARTX=$PWD/kpartx elif [[ -f "$PWD/kpartx/kpartx" && -x "$PWD/kpartx/kpartx" ]]; then @@ -101,7 +104,7 @@ step preparation KPARTX=$(which kpartx) fi } -[[ $KPARTX ]] +[[ "$KPARTX" ]] echo "== Using kpartx = $KPARTX ==" >&2 # Try to use system kpartx for cleanup @@ -118,18 +121,16 @@ SECTSIZ=512 OFFS=32 # offset of linear mapping into dev, sectors VG=__kpvg__ # volume group name LV=__kplv__ # logical vol name - - OK= -[[ $WORKDIR ]] || { +[[ "$WORKDIR" ]] || { WORKDIR=$(mktemp -d /tmp/kpartx-XXXXXX) - push_cleanup 'rm -rf $WORKDIR' + # shellcheck disable=SC2016 + push_cleanup 'rm -rf "$WORKDIR"' } -USE_DEVICESFILE= -if [[ "$((lvmconfig --typeconfig full 2>/dev/null || true) | \ - sed -n 's/.*use_devicesfile=//p')" = 1 ]]; then +if [[ "$( (lvmconfig --typeconfig full 2>/dev/null || true) | \ + sed -n 's/.*use_devicesfile=//p' )" = 1 ]]; then LVMCONF= # This test may modify the devices file. Be sure to restore it if [[ -f /etc/lvm/devices/system.devices ]]; then @@ -139,33 +140,36 @@ if [[ "$((lvmconfig --typeconfig full 2>/dev/null || true) | \ push_cleanup 'rm -f /etc/lvm/devices/system.devices' fi else + # This isn't shell code, it's actually a single lvm command argument. + # shellcheck disable=SC2089 LVMCONF='devices { filter = [ "a|/dev/loop.*|", r".*" ] }' fi push_cleanup "cd $PWD" +# If cd fails, the script will terminate +# shellcheck disable=SC2164 cd "$WORKDIR" step "create loop devices" -truncate -s $SIZE $FILE1 -truncate -s $SIZE $FILE2 -truncate -s $SIZE $FILE3 -truncate -s $SIZE $FILE4 - -LO1=$(losetup -f $FILE1 --show) -push_cleanup 'losetup -d $LO1' -LO2=$(losetup -f $FILE2 --show) -push_cleanup 'losetup -d $LO2' -LO3=$(losetup -f $FILE3 --show) -push_cleanup 'losetup -d $LO3' -LO4=$(losetup -f $FILE4 --show) -push_cleanup 'losetup -d $LO4' - -[[ $LO1 && $LO2 && $LO3 && $LO4 && -b $LO1 && -b $LO2 && -b $LO3 && -b $LO4 ]] -DEV1=$(stat -c "%t:%T" $LO1) -DEV2=$(stat -c "%t:%T" $LO2) -DEV3=$(stat -c "%t:%T" $LO3) - -usleep $WAIT_US +truncate -s "$SIZE" "$FILE1" +truncate -s "$SIZE" "$FILE2" +truncate -s "$SIZE" "$FILE3" +truncate -s "$SIZE" "$FILE4" + +LO1=$(losetup -f "$FILE1" --show) +push_cleanup 'losetup -d "$LO1"' +LO2=$(losetup -f "$FILE2" --show) +push_cleanup 'losetup -d "$LO2"' +LO3=$(losetup -f "$FILE3" --show) +push_cleanup 'losetup -d "$LO3"' +LO4=$(losetup -f "$FILE4" --show) +push_cleanup 'losetup -d "$LO4"' + +[[ "$LO1" && "$LO2" && "$LO3" && "$LO4" && -b "$LO1" && -b "$LO2" && -b "$LO3" && -b "$LO4" ]] +DEV1=$(stat -c "%t:%T" "$LO1") +DEV2=$(stat -c "%t:%T" "$LO2") + +usleep "$WAIT_US" step "create DM devices (spans)" # Create two linear mappings spanning two loopdevs. @@ -178,225 +182,249 @@ TABLE="\ 0 $((SIZE/SECTSIZ-OFFS)) linear $DEV1 $OFFS $((SIZE/SECTSIZ-OFFS)) $((SIZE/SECTSIZ-OFFS)) linear $DEV2 $OFFS" -SPAN2=$(basename $LO2) -dmsetup create $SPAN1 <<<"$TABLE" -push_cleanup 'dmsetup remove -f $SPAN1' # This must end in a normal letter SPAN1=__kpt__ +SPAN2=$(basename "$LO2") +dmsetup create "$SPAN1" <<<"$TABLE" +push_cleanup 'dmsetup remove -f "$SPAN1"' -dmsetup create $SPAN2 <<<"$TABLE" -push_cleanup 'dmsetup remove -f $SPAN2' +dmsetup create "$SPAN2" <<<"$TABLE" +push_cleanup 'dmsetup remove -f "$SPAN2"' # This is a non-kpartx pseudo "partition" mapping USER1=user1 -push_cleanup 'dmsetup remove -f $USER1' -dmsetup create $USER1 <&2 -usleep $WAIT_US +# shellcheck disable=2086 +$KPARTX $KPARTX_OPTS -d "/dev/mapper/$SPAN1" >&2 +usleep "$WAIT_US" current_state -[[ -b $SPAN2P1 ]] -[[ -b $LO1P1 ]] -[[ -b $LO2P1 ]] -[[ ! -b $SPAN1P1 ]] +[[ -b "$SPAN2P1" ]] +[[ -b "$LO1P1" ]] +[[ -b "$LO2P1" ]] +[[ ! -b "$SPAN1P1" ]] -$KPARTX $KPARTX_OPTS -d /dev/mapper/$SPAN2 -usleep $WAIT_US +# shellcheck disable=2086 +$KPARTX $KPARTX_OPTS -d "/dev/mapper/$SPAN2" +usleep "$WAIT_US" current_state -[[ -b $LO1P1 ]] -[[ -b $LO2P1 ]] -[[ ! -b $SPAN2P1 ]] +[[ -b "$LO1P1" ]] +[[ -b "$LO2P1" ]] +[[ ! -b "$SPAN2P1" ]] step "rename partitions on loop device" -$KPARTX $KPARTX_OPTS -u -p -spam $LO2 +# shellcheck disable=2086 +$KPARTX $KPARTX_OPTS -u -p -spam "$LO2" current_state -[[ ! -b ${LO2P1} ]] -[[ -b ${LO2P1//-foo/-spam} ]] +[[ ! -b "$LO2P1" ]] +[[ -b "${LO2P1//-foo/-spam}" ]] step "rename partitions on loop device back" -$KPARTX $KPARTX_OPTS -u -p -foo $LO2 +# shellcheck disable=2086 +$KPARTX $KPARTX_OPTS -u -p -foo "$LO2" current_state -[[ -b ${LO2P1} ]] -[[ ! -b ${LO2P1//-foo/-spam} ]] +[[ -b "$LO2P1" ]] +[[ ! -b "${LO2P1//-foo/-spam}" ]] step "rename partitions on loop device to default" -$KPARTX $KPARTX_OPTS -u $LO2 +# shellcheck disable=2086 +$KPARTX $KPARTX_OPTS -u "$LO2" #read a current_state -[[ ! -b ${LO2P1} ]] +[[ ! -b "$LO2P1" ]] # $LO1 ends in a digit -[[ -b ${LO2P1//-foo/p} ]] +[[ -b "${LO2P1//-foo/p}" ]] step "rename partitions on loop device back from default" -$KPARTX $KPARTX_OPTS -u -p -foo $LO2 +# shellcheck disable=2086 +$KPARTX $KPARTX_OPTS -u -p -foo "$LO2" current_state -[[ -b ${LO2P1} ]] -[[ ! -b ${LO2P1//-foo/p} ]] +[[ -b "$LO2P1" ]] +[[ ! -b "${LO2P1//-foo/p}" ]] step "rename partitions on loop devices" -$KPARTX $KPARTX_OPTS -u -p spam $LO2 +# shellcheck disable=2086 +$KPARTX $KPARTX_OPTS -u -p spam "$LO2" step "delete partitions on loop devices" -$KPARTX $KPARTX_OPTS -d $LO3 +# shellcheck disable=2086 +$KPARTX $KPARTX_OPTS -d "$LO3" # This will also delete the loop device -$KPARTX $KPARTX_OPTS -d $FILE2 -$KPARTX $KPARTX_OPTS -d $LO1 -usleep $WAIT_US +# shellcheck disable=2086 +$KPARTX $KPARTX_OPTS -d "$FILE2" +# shellcheck disable=2086 +$KPARTX $KPARTX_OPTS -d "$LO1" +usleep "$WAIT_US" # ls -l /dev/mapper current_state -[[ ! -b $LO1P1 ]] -# 'dmsetup remove -f $(basename $SPAN1P1)' +[[ ! -b "$LO1P1" ]] +# 'dmsetup remove -f $(basename "$SPAN1P1")' pop_cleanup -[[ ! -b $LO2P1 ]] +[[ ! -b "$LO2P1" ]] # '$CLEANUP_KPARTX $KPARTX_OPTS -f -d /dev/mapper/$SPAN1' pop_cleanup # spans should not have been removed current_state -[[ -b /dev/mapper/$SPAN1 ]] -[[ -b /dev/mapper/$SPAN2 ]] -[[ -b /dev/mapper/$USER1 ]] +[[ -b "/dev/mapper/$SPAN1" ]] +[[ -b "/dev/mapper/$SPAN2" ]] +[[ -b "/dev/mapper/$USER1" ]] # LVs neither -[[ -b /dev/mapper/$VG-$LV ]] +[[ -b "/dev/mapper/$VG-$LV" ]] step "delete partitions on $LO3 with -f" -$KPARTX $KPARTX_OPTS -f -d $LO3 +# shellcheck disable=2086 +$KPARTX $KPARTX_OPTS -f -d "$LO3" # -d -f should delete the LV, too -[[ ! -b /dev/mapper/$VG-$LV ]] -[[ -b /dev/mapper/$SPAN1 ]] -[[ -b /dev/mapper/$SPAN2 ]] +[[ ! -b "/dev/mapper/$VG-$LV" ]] +[[ -b "/dev/mapper/$SPAN1" ]] +[[ -b "/dev/mapper/$SPAN2" ]] step "test kpartx creation/deletion on an image file with no existing loopdev" -losetup -d $LO4 +losetup -d "$LO4" -OUTPUT=$($KPARTX $KPARTX_OPTS -v -a $FILE4 2>&1) -read loop dm < \ +# shellcheck disable=2086 +OUTPUT=$($KPARTX $KPARTX_OPTS -v -a "$FILE4" 2>&1) +read -r loop dm < \ <(sed -n 's/^add map \(loop[0-9]*\)p1 ([0-9]*:\([0-9]*\)).*$/\1 dm-\2/p' \ - <<<$OUTPUT) -[[ $dm && $loop ]] -push_cleanup "dmsetup remove -f /dev/$dm" -push_cleanup "losetup -d /dev/$loop" - -[[ -b /dev/mapper/${loop}p1 ]] -$KPARTX -d $KPARTX_OPTS $FILE4 -[[ ! -b /dev/mapper/${loop}p1 ]] + <<<"$OUTPUT") +[[ "$dm" && "$loop" ]] +push_cleanup 'dmsetup remove -f "/dev/$dm"' +push_cleanup 'losetup -d "/dev/$loop"' + +[[ -b "/dev/mapper/${loop}p1" ]] +# shellcheck disable=2086 +$KPARTX -d $KPARTX_OPTS "$FILE4" +[[ ! -b "/dev/mapper/${loop}p1" ]] # /dev/$loop is _not_ automatically deleted -[[ -b /dev/${loop} ]] +[[ -b "/dev/${loop}" ]] OK=yes From 48d34b688cbe14defba42c51c250be9ae62ec866 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 28 Jan 2026 23:04:33 +0100 Subject: [PATCH 10/50] test-kpartx: make partition renaming test more robust Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index 5bce02b85..46c37a407 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -92,6 +92,20 @@ current_state() { dmsetup ls } + +# Default name of partition device +partname() { + local base + base=${1##*/} + + # Is the last character a digit? + if [[ "${base:$((${#base}-1))}" =~ [0-9] ]]; then + printf "/dev/mapper/%sp%s" "$base" "$2" + else + printf "/dev/mapper/%s%s" "$base" "$2" + fi +} + step preparation [[ "$UID" -eq 0 ]] @@ -302,20 +316,20 @@ current_state [[ -b "$SPAN2P1" ]] [[ -b "$SPAN1P1" ]] -step "rename partitions on DM device to default" +step "rename partitions on DM device with default delimiter" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -u "/dev/mapper/$SPAN1" current_state [[ ! -b "$SPAN1P1" ]] # This assumes that $SPAN1 ends in a non-digit -[[ -b "${SPAN1P1//-spam/}" ]] +[[ -b "$(partname "$SPAN1" 1)" ]] -step "rename partitions on DM device back from default" +step "rename partitions on DM device with delimiter -spam" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -u -p -spam "/dev/mapper/$SPAN1" current_state [[ -b "$SPAN1P1" ]] -[[ ! -b "${SPAN1P1//-foo/}" ]] +[[ ! -b "$(partname "$SPAN1" 1)" ]] step "delete partitions on DM devices" # shellcheck disable=2086 @@ -337,35 +351,34 @@ current_state [[ -b "$LO2P1" ]] [[ ! -b "$SPAN2P1" ]] -step "rename partitions on loop device" +step "rename partitions on loop device with delimiter -spam" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -u -p -spam "$LO2" current_state [[ ! -b "$LO2P1" ]] [[ -b "${LO2P1//-foo/-spam}" ]] -step "rename partitions on loop device back" +step "rename partitions on loop device with delimiter -foo" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -u -p -foo "$LO2" current_state [[ -b "$LO2P1" ]] [[ ! -b "${LO2P1//-foo/-spam}" ]] -step "rename partitions on loop device to default" +step "rename partitions on loop device with default delimiter" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -u "$LO2" #read a current_state [[ ! -b "$LO2P1" ]] -# $LO1 ends in a digit -[[ -b "${LO2P1//-foo/p}" ]] +[[ -b "$(partname "$LO2" 1)" ]] -step "rename partitions on loop device back from default" +step "rename partitions on loop device with delimiter -foo" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -u -p -foo "$LO2" current_state [[ -b "$LO2P1" ]] -[[ ! -b "${LO2P1//-foo/p}" ]] +[[ ! -b "$(partname "$LO2" 1)" ]] step "rename partitions on loop devices" # shellcheck disable=2086 From 355e99a79f3aab099023dc6d2d719ad84cdf79f1 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 28 Jan 2026 23:45:21 +0100 Subject: [PATCH 11/50] test-kpartx: use "command -v" instead of "which" "which" may not always be awailable. Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index 46c37a407..d3c61dd31 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -115,7 +115,7 @@ step preparation elif [[ -f "$PWD/kpartx/kpartx" && -x "$PWD/kpartx/kpartx" ]]; then KPARTX=$PWD/kpartx/kpartx else - KPARTX=$(which kpartx) + KPARTX=$(command -v kpartx) fi } [[ "$KPARTX" ]] @@ -123,8 +123,8 @@ echo "== Using kpartx = $KPARTX ==" >&2 # Try to use system kpartx for cleanup # Fall back to ours if not found -CLEANUP_KPARTX=$(which kpartx) -[[ "$CLEANUP_KPARTX" ]] || CLEANUP_KPARTX=$KPARTX +# shellcheck disable=SC2034 +CLEANUP_KPARTX=$(command -v kpartx) || CLEANUP_KPARTX=$KPARTX FILE1=kpartx1 FILE2=kpartx2 From f125160ba3f27b1b3a7d0398cc877971567264fd Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 28 Jan 2026 23:46:10 +0100 Subject: [PATCH 12/50] test-kpartx: use sleep for microsecond sleep The usleep command may not always be available. Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index d3c61dd31..22bea726d 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -106,6 +106,10 @@ partname() { fi } +wait_a_moment() { + sleep "$(printf %d.%06d "$((WAIT_USEC / 1000000))" "$((WAIT_USEC % 1000000))")" +} + step preparation [[ "$UID" -eq 0 ]] @@ -183,7 +187,7 @@ push_cleanup 'losetup -d "$LO4"' DEV1=$(stat -c "%t:%T" "$LO1") DEV2=$(stat -c "%t:%T" "$LO2") -usleep "$WAIT_US" +wait_a_moment step "create DM devices (spans)" # Create two linear mappings spanning two loopdevs. @@ -212,7 +216,7 @@ dmsetup create "$USER1" <&2 -usleep "$WAIT_US" +wait_a_moment current_state [[ -b "$SPAN2P1" ]] @@ -344,7 +348,7 @@ current_state # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -d "/dev/mapper/$SPAN2" -usleep "$WAIT_US" +wait_a_moment current_state [[ -b "$LO1P1" ]] @@ -394,7 +398,7 @@ $KPARTX $KPARTX_OPTS -d "$LO3" $KPARTX $KPARTX_OPTS -d "$FILE2" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -d "$LO1" -usleep "$WAIT_US" +wait_a_moment # ls -l /dev/mapper current_state From 207e40c639b5f5c845b6bf72a6cca764481dc290 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 29 Jan 2026 21:26:20 +0100 Subject: [PATCH 13/50] test-kpartx: also add trap for signals Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index 22bea726d..a3e8a2c40 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -42,7 +42,7 @@ trap 'LINE=$LINENO trap - ERR eval echo "$0: == error in \\\"\$BASH_COMMAND\\\" \(\\\"$BASH_COMMAND\\\"\) on line $LINE ==" >&2 -exit 1' ERR +exit 1' ERR INT TERM trap 'cleanup' 0 CLEANUP=: From ee36b61a27fb1891a46eea77ab2b15edb2c4f8af Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 29 Jan 2026 21:26:20 +0100 Subject: [PATCH 14/50] test-kpartx: use sfdisk instead of parted parted has functionalilty to create partition mappings, which is not desired in our case. Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index a3e8a2c40..4dae39cbe 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -78,8 +78,14 @@ step() { } mk_partitions() { - parted -s "$1" mklabel msdos - parted -s -- "$1" mkpart prim ext2 1MiB -1s + # prefer sfdisk, as parted will try to create + # partition mappings by itself + if command -v sfdisk &>/dev/null; then + printf ",+,\n" | sfdisk -f "$1" &>/dev/null + else + parted -s "$1" mklabel msdos + parted -s -- "$1" mkpart prim ext2 1MiB -1s + fi } wipe_ptable() { From 6acfa4bbcc7da091deebdae576185e5778ab05c5 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 29 Jan 2026 21:31:04 +0100 Subject: [PATCH 15/50] test-kpartx: improve step descriptions to ease debugging Signed-off-by: Martin Wilck --- kpartx/test-kpartx | 82 ++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index 4dae39cbe..32a5aef01 100755 --- a/kpartx/test-kpartx +++ b/kpartx/test-kpartx @@ -195,7 +195,11 @@ DEV2=$(stat -c "%t:%T" "$LO2") wait_a_moment -step "create DM devices (spans)" +SPAN1=__kpt__ +SPAN2=$(basename "$LO2") +# This is a non-kpartx pseudo "partition" mapping +USER1=user1 +step "create DM devices => $SPAN1, $SPAN2, $USER1" # Create two linear mappings spanning two loopdevs. # One of them gets a pathological name colliding with # the loop device name. @@ -206,17 +210,12 @@ TABLE="\ 0 $((SIZE/SECTSIZ-OFFS)) linear $DEV1 $OFFS $((SIZE/SECTSIZ-OFFS)) $((SIZE/SECTSIZ-OFFS)) linear $DEV2 $OFFS" -# This must end in a normal letter -SPAN1=__kpt__ -SPAN2=$(basename "$LO2") dmsetup create "$SPAN1" <<<"$TABLE" push_cleanup 'dmsetup remove -f "$SPAN1"' dmsetup create "$SPAN2" <<<"$TABLE" push_cleanup 'dmsetup remove -f "$SPAN2"' -# This is a non-kpartx pseudo "partition" mapping -USER1=user1 push_cleanup 'dmsetup remove -f "$USER1"' dmsetup create "$USER1" < $VG-$LV, $USER1" # On the 3rd loop device, we create a VG and an LV # The LV should not be removed by kpartx. # shellcheck disable=SC2090 @@ -246,38 +245,38 @@ current_state # dmsetup table /dev/mapper/$VG-$LV # dmsetup info /dev/mapper/$VG-$LV -step "create partitions on loop devices" +step "create partitions on loop devices => $LO1 $LO2 $LO4" mk_partitions "$LO1" mk_partitions "$LO2" mk_partitions "$LO4" -step "run kpartx on regular file" # Test invocation of kpartx with regular file here LO2P1=/dev/mapper/$(basename "$LO2")-foo1 push_cleanup 'dmsetup remove -f "$(basename $LO2P1)"' +step "run kpartx on regular file $FILE2 => $LO2P1" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -a -p -foo "$FILE2" current_state [[ -b "$LO2P1" ]] -step "remove partitions with deleted ptable" +step "deleting partition table on $LO2 => -$LO2P1" wipe_ptable "$LO2" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -d "$LO2" current_state [[ ! -b "$LO2P1" ]] -step "re-add just removed partions" +step "re-add just removed partions on $LO2 => $LO2P1" mk_partitions "$LO2" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -a -p -foo "$FILE2" current_state [[ -b "$LO2P1" ]] -step "run kpartx on loop device" LO1P1=/dev/mapper/$(basename "$LO1")-eggs1 +step "run kpartx on loop device $LO1 => $LO1P1" push_cleanup 'dmsetup remove -f $(basename "$LO1P1")' # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -a -p -eggs "$LO1" @@ -289,7 +288,7 @@ current_state # dmsetup info $LO2P1 -step "rename $(basename "$LO1P1") to $(basename "$LO1")" +step "rename $(basename "$LO1P1") -> $(basename "$LO1")" # Set pathological name for partition on $LO1 (same as loop device itself) dmsetup rename "$(basename "$LO1P1")" "$(basename "$LO1")" LO1P1=/dev/mapper/$(basename "$LO1") @@ -299,14 +298,14 @@ push_cleanup 'dmsetup remove -f "$(basename "$LO1P1")"' current_state [[ -b "$LO1P1" ]] -# dmsetup info $LO1P1 - -step "create partitions on DM devices" mk_partitions "/dev/mapper/$SPAN2" - +SPAN2P1=/dev/mapper/${SPAN2}-bar1 +step "create partitions on DM device $SPAN2 => $SPAN2P1" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -a -p -bar "/dev/mapper/$SPAN2" -SPAN2P1=/dev/mapper/${SPAN2}-bar1 +wait_a_moment +current_state +[[ -b "$SPAN2P1" ]] # udev rules may have created partition mappings without UUIDs # which aren't removed by default (if system standard kpartx doesn't @@ -314,6 +313,7 @@ SPAN2P1=/dev/mapper/${SPAN2}-bar1 push_cleanup '$CLEANUP_KPARTX $KPARTX_OPTS -f -d "/dev/mapper/$SPAN2"' push_cleanup 'dmsetup remove -f "$(basename "$SPAN2P1")"' +step "create partitions on DM device $SPAN1 => $SPAN1P1" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -a -p -spam "/dev/mapper/$SPAN1" SPAN1P1=/dev/mapper/${SPAN1}-spam1 @@ -326,7 +326,7 @@ current_state [[ -b "$SPAN2P1" ]] [[ -b "$SPAN1P1" ]] -step "rename partitions on DM device with default delimiter" +step "rename partitions on $SPAN1 with default delimiter => $SPAN1P1 -> $(partname "$SPAN1" 1)" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -u "/dev/mapper/$SPAN1" current_state @@ -334,14 +334,14 @@ current_state # This assumes that $SPAN1 ends in a non-digit [[ -b "$(partname "$SPAN1" 1)" ]] -step "rename partitions on DM device with delimiter -spam" +step "rename partitions on DM device with delimiter -spam => $(partname "$SPAN1" 1) -> $SPAN1P1" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -u -p -spam "/dev/mapper/$SPAN1" current_state [[ -b "$SPAN1P1" ]] [[ ! -b "$(partname "$SPAN1" 1)" ]] -step "delete partitions on DM devices" +step "delete partitions on DM device $SPAN1 => -$SPAN1P1" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -d "/dev/mapper/$SPAN1" >&2 wait_a_moment @@ -352,68 +352,78 @@ current_state [[ -b "$LO2P1" ]] [[ ! -b "$SPAN1P1" ]] +step "delete partitions on DM device $SPAN2 => -$SPAN2P1" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -d "/dev/mapper/$SPAN2" wait_a_moment - current_state [[ -b "$LO1P1" ]] [[ -b "$LO2P1" ]] [[ ! -b "$SPAN2P1" ]] -step "rename partitions on loop device with delimiter -spam" +step "rename on $LO2 with delimiter -spam => $LO2P1 -> ${LO2P1//-foo/-spam}" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -u -p -spam "$LO2" +wait_a_moment current_state [[ ! -b "$LO2P1" ]] [[ -b "${LO2P1//-foo/-spam}" ]] -step "rename partitions on loop device with delimiter -foo" +step "rename partitions on $LO2 with delimiter -foo => ${LO2P1//-foo/-spam} -> $LO2P1" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -u -p -foo "$LO2" +wait_a_moment current_state [[ -b "$LO2P1" ]] [[ ! -b "${LO2P1//-foo/-spam}" ]] -step "rename partitions on loop device with default delimiter" +step "rename partitions on $LO2 with default delimiter => $LO2P1 -> $(partname "$LO2" 1)" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -u "$LO2" -#read a +wait_a_moment current_state [[ ! -b "$LO2P1" ]] [[ -b "$(partname "$LO2" 1)" ]] -step "rename partitions on loop device with delimiter -foo" +step "rename partitions on $LO2 with delimiter -foo => $(partname "$LO2" 1) -> $LO2P1" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -u -p -foo "$LO2" current_state [[ -b "$LO2P1" ]] [[ ! -b "$(partname "$LO2" 1)" ]] -step "rename partitions on loop devices" +step "rename partitions on $LO2 with delimiter spam => $LO2P1 -> ${LO2P1//-foo/spam} " # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -u -p spam "$LO2" +wait_a_moment +current_state +[[ ! -b "$LO2P1" ]] +[[ -b "${LO2P1//-foo/spam}" ]] -step "delete partitions on loop devices" - +step "delete partitions on loop device $LO3" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -d "$LO3" +wait_a_moment +current_state +step "delete partitions on file $FILE2 / $LO2" # This will also delete the loop device # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -d "$FILE2" +wait_a_moment +current_state + +step "delete partitions on file $LO1" # shellcheck disable=2086 $KPARTX $KPARTX_OPTS -d "$LO1" wait_a_moment - -# ls -l /dev/mapper current_state [[ ! -b "$LO1P1" ]] -# 'dmsetup remove -f $(basename "$SPAN1P1")' -pop_cleanup + +pop_cleanup # 'dmsetup remove -f $(basename "$SPAN1P1")' [[ ! -b "$LO2P1" ]] -# '$CLEANUP_KPARTX $KPARTX_OPTS -f -d /dev/mapper/$SPAN1' -pop_cleanup +pop_cleanup # '$CLEANUP_KPARTX $KPARTX_OPTS -f -d /dev/mapper/$SPAN1' + # spans should not have been removed current_state [[ -b "/dev/mapper/$SPAN1" ]] From b1abfa8b6d8678945c03961281fc1e43d9677036 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 28 Jan 2026 23:04:52 +0100 Subject: [PATCH 16/50] kpartx/Makefile: add test target to run test-kpartx This test must be run as root. Signed-off-by: Martin Wilck --- kpartx/Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kpartx/Makefile b/kpartx/Makefile index 849fd36d0..fe83e2905 100644 --- a/kpartx/Makefile +++ b/kpartx/Makefile @@ -40,8 +40,13 @@ uninstall: $(Q)$(RM) $(DESTDIR)$(libudevdir)/rules.d/67-kpartx-compat.rules $(Q)$(RM) $(DESTDIR)$(libudevdir)/rules.d/68-del-part-nodes.rules +kpartx.out: $(EXEC) + ./test-kpartx >$@ 2>&1 || { cat $@; rm -f $@; exit 1; } + +test: kpartx.out + clean: dep_clean - $(Q)$(RM) core *.o $(EXEC) kpartx.rules + $(Q)$(RM) core *.o $(EXEC) kpartx.rules kpartx.out include $(wildcard $(OBJS:.o=.d)) From e32c00153959e1d3abde3aaf28990f253daa0cb8 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 29 Jan 2026 18:03:00 +0100 Subject: [PATCH 17/50] kpartx/Makefile: enable debugging for test-kpartx Signed-off-by: Martin Wilck --- kpartx/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/kpartx/Makefile b/kpartx/Makefile index fe83e2905..cdadaeca8 100644 --- a/kpartx/Makefile +++ b/kpartx/Makefile @@ -43,6 +43,7 @@ uninstall: kpartx.out: $(EXEC) ./test-kpartx >$@ 2>&1 || { cat $@; rm -f $@; exit 1; } +test: export KPARTX_DEBUG := 1 test: kpartx.out clean: dep_clean From 60183a1773ce72dc1774747bdfaf7c634c1b141e Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Fri, 10 Jul 2026 11:25:57 +0200 Subject: [PATCH 18/50] Add github workflows (pre-0.15) on new orphan branch We will use this branch to maintain the workflows separately from the actual code. That makes maintenance of the workflows easier for the stable branches. Signed-off-by: Martin Wilck --- .github/workflows/abi-stable.yaml | 122 +++++++++++ .github/workflows/abi.yaml | 76 +++++++ .github/workflows/build-and-unittest.yaml | 114 +++++++++++ .github/workflows/codingstyle.yml | 43 ++++ .github/workflows/coverity.yaml | 52 +++++ .github/workflows/foreign.yaml | 183 +++++++++++++++++ .github/workflows/multiarch-stable.yaml | 98 +++++++++ .github/workflows/multiarch.yaml | 113 ++++++++++ .github/workflows/native.yaml | 239 ++++++++++++++++++++++ .github/workflows/rolling.yaml | 96 +++++++++ 10 files changed, 1136 insertions(+) create mode 100644 .github/workflows/abi-stable.yaml create mode 100644 .github/workflows/abi.yaml create mode 100644 .github/workflows/build-and-unittest.yaml create mode 100644 .github/workflows/codingstyle.yml create mode 100644 .github/workflows/coverity.yaml create mode 100644 .github/workflows/foreign.yaml create mode 100644 .github/workflows/multiarch-stable.yaml create mode 100644 .github/workflows/multiarch.yaml create mode 100644 .github/workflows/native.yaml create mode 100644 .github/workflows/rolling.yaml diff --git a/.github/workflows/abi-stable.yaml b/.github/workflows/abi-stable.yaml new file mode 100644 index 000000000..de8050ab0 --- /dev/null +++ b/.github/workflows/abi-stable.yaml @@ -0,0 +1,122 @@ +name: check-abi for stable branch +on: + push: + branches: + - 'stable-*' + paths: + - '.github/workflows/abi-stable.yaml' + - '**.h' + - '**.c' + - '**.version' + pull_request: + branches: + - 'stable-*' + workflow_dispatch: + +jobs: + reference-abi: + runs-on: ubuntu-24.04 + steps: + - name: get parent tag (push) + run: > + echo ${{ github.ref }} | + sed -E 's,refs/heads/stable-([0-9]\.[0-9]*)\.y,PARENT_TAG=\1.0,' >> $GITHUB_ENV + if: github.event_name == 'push' + - name: get parent tag (PR) + run: > + echo ${{ github.base_ref }} | + sed -E 's,stable-([0-9]\.[0-9]*)\.y,PARENT_TAG=\1.0,' >> $GITHUB_ENV + if: github.event_name == 'pull_request' + - name: assert parent tag + run: /bin/false + if: env.PARENT_TAG == '' + - name: try to download ABI for ${{ env.PARENT_TAG }} + id: download_abi + continue-on-error: true + uses: dawidd6/action-download-artifact@v6 + with: + workflow: abi-stable.yaml + workflow_conclusion: '' + branch: ${{ github.ref_name }} + name: multipath-abi-${{ env.PARENT_TAG }} + search_artifacts: true + path: __unused__ + - name: update + if: steps.download_abi.outcome != 'success' + run: sudo apt-get update + - name: dependencies + if: steps.download_abi.outcome != 'success' + run: > + sudo apt-get install --yes gcc + gcc make pkg-config abigail-tools + libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev + libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev + libmount-dev + - name: checkout ${{ env.PARENT_TAG }} + if: steps.download_abi.outcome != 'success' + uses: actions/checkout@v4 + with: + ref: ${{ env.PARENT_TAG }} + - name: build ABI for ${{ env.PARENT_TAG }} + if: steps.download_abi.outcome != 'success' + run: make -j$(nproc) -Orecurse abi + - name: save ABI + if: steps.download_abi.outcome != 'success' + uses: actions/upload-artifact@v4 + with: + name: multipath-abi-${{ env.PARENT_TAG }} + path: abi + + check-abi: + runs-on: ubuntu-24.04 + needs: reference-abi + steps: + - name: get parent tag (push) + run: > + echo ${{ github.ref }} | + sed -E 's,refs/heads/stable-([0-9]\.[0-9]*)\.y,PARENT_TAG=\1.0,' >> $GITHUB_ENV + if: github.event_name == 'push' + - name: get parent tag (PR) + run: > + echo ${{ github.base_ref }} | + sed -E 's,stable-([0-9]\.[0-9]*)\.y,PARENT_TAG=\1.0,' >> $GITHUB_ENV + if: github.event_name == 'pull_request' + - name: assert parent tag + run: /bin/false + if: env.PARENT_TAG == '' + - name: checkout ${{ github.ref }} + uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + - name: download ABI for ${{ env.PARENT_TAG }} + id: download_abi + uses: dawidd6/action-download-artifact@v6 + with: + workflow: abi-stable.yaml + workflow_conclusion: '' + branch: ${{ github.ref_name }} + name: multipath-abi-${{ env.PARENT_TAG }} + search_artifacts: true + path: reference-abi + - name: update + run: sudo apt-get update + - name: dependencies + run: > + sudo apt-get install --yes gcc + gcc make pkg-config abigail-tools + libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev + libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev + libmount-dev + - name: check ABI of ${{ github.ref }} against ${{ env.PARENT_TAG }} + id: check_abi + run: make -j$(nproc) -Orecurse abi-test + continue-on-error: true + - name: save differences + if: steps.check_abi.outcome != 'success' + uses: actions/upload-artifact@v4 + with: + name: abi-test + path: abi-test + - name: fail + run: /bin/false + if: steps.check_abi.outcome != 'success' diff --git a/.github/workflows/abi.yaml b/.github/workflows/abi.yaml new file mode 100644 index 000000000..1a7d3e5dd --- /dev/null +++ b/.github/workflows/abi.yaml @@ -0,0 +1,76 @@ +name: check-abi +on: + push: + branches: + - master + - queue + - abi + paths: + - '.github/workflows/abi.yaml' + - '**.h' + - '**.c' + pull_request: + branches: + - master + - queue + workflow_dispatch: +env: + ABI_BRANCH: ${{ secrets.ABI_BRANCH }} + +jobs: + save-and-test-ABI: + runs-on: ubuntu-24.04 + steps: + - name: set ABI branch + if: env.ABI_BRANCH == '' + run: echo "ABI_BRANCH=master" >> $GITHUB_ENV + - name: checkout + uses: actions/checkout@v4 + - name: get reference ABI + id: reference + continue-on-error: true + uses: dawidd6/action-download-artifact@v6 + with: + workflow: abi.yaml + branch: ${{ env.ABI_BRANCH }} + name: abi + path: reference-abi + - name: update + run: sudo apt-get update + - name: dependencies + run: > + sudo apt-get install --yes gcc + gcc make pkg-config abigail-tools + libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev + libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev + libmount-dev + - name: create ABI + run: make -Orecurse -j$(nproc) abi.tar.gz + - name: save ABI + uses: actions/upload-artifact@v4 + with: + name: abi + path: abi + overwrite: true + - name: compare ABI against reference + id: compare + continue-on-error: true + if: steps.reference.outcome == 'success' + run: make abi-test + - name: save differences + if: steps.compare.outcome == 'failure' + uses: actions/upload-artifact@v4 + with: + name: abi-test + path: abi-test + overwrite: true + + - name: fail + # MUST use >- here, otherwise the condition always evaluates to true + if: >- + ${{ + env.ABI_BRANCH != github.ref_name && + (steps.reference.outcome == 'failure' || + steps.compare.outcome == 'failure') + }} + run: false diff --git a/.github/workflows/build-and-unittest.yaml b/.github/workflows/build-and-unittest.yaml new file mode 100644 index 000000000..616a4de1f --- /dev/null +++ b/.github/workflows/build-and-unittest.yaml @@ -0,0 +1,114 @@ +name: basic-build-and-ci +on: + push: + branches: + - master + - queue + - tip + - 'stable-*' + pull_request: + branches: + - master + - queue + - 'stable-*' +jobs: + jammy: + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + rl: ['', 'libreadline', 'libedit'] + cc: [ gcc, clang ] + steps: + - uses: actions/checkout@v4 + - name: update + run: sudo apt-get update + - name: dependencies + run: > + sudo apt-get install --yes gcc + make pkg-config valgrind + libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev + libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev + libmount-dev linux-modules-extra-$(uname -r) + - name: mpath + run: sudo modprobe dm_multipath + - name: zram + run: sudo modprobe zram num_devices=0 + - name: zram-device + run: echo ZRAM=$(sudo cat /sys/class/zram-control/hot_add) >> $GITHUB_ENV + - name: set-zram-size + run: echo 1G | sudo tee /sys/block/zram$ZRAM/disksize + - name: set CC + run: echo CC=${{ matrix.cc }} >> $GITHUB_ENV + - name: set optflags + # valgrind doesn't support the dwarf-5 format of clang 14 + run: echo OPT='-O2 -gdwarf-4 -fstack-protector-strong' >> $GITHUB_ENV + if: matrix.cc == 'clang' + - name: build + run: > + make -Orecurse -j$(nproc) + READLINE=${{ matrix.rl }} OPTFLAGS="$OPT" + - name: test + run: > + make -Orecurse -j$(nproc) + OPTFLAGS="$OPT" test + - name: valgrind-test + id: valgrind + run: > + make -Orecurse -j$(nproc) + OPTFLAGS="$OPT" valgrind-test + continue-on-error: true + - name: valgrind-results + run: cat tests/*.vgr + - name: fail if valgrind failed + run: /bin/false + if: steps.valgrind.outcome != 'success' + - name: clean-nonroot-artifacts + run: rm -f tests/dmevents.out tests/directio.out + - name: root-test + run: sudo make DIO_TEST_DEV=/dev/zram$ZRAM test + - name: kpartx-test + run: sudo make -C kpartx test + noble: + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + rl: ['', 'libreadline', 'libedit'] + cc: [ gcc, clang ] + steps: + - uses: actions/checkout@v4 + - name: mpath + run: sudo modprobe dm_multipath + - name: brd + run: sudo modprobe brd rd_nr=1 rd_size=65536 + - name: update + run: sudo apt-get update + - name: dependencies + run: > + sudo apt-get install --yes gcc-10 + make pkg-config valgrind + libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev + libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev + libmount-dev linux-modules-extra-$(uname -r) + - name: set CC + run: echo CC=${{ matrix.cc }} >> $GITHUB_ENV + - name: build + run: make -Orecurse -j$(nproc) READLINE=${{ matrix.rl }} + - name: test + run: make -Orecurse -j$(nproc) test + - name: valgrind-test + id: valgrind + run: make -Orecurse -j$(nproc) valgrind-test + continue-on-error: true + - name: valgrind-results + run: cat tests/*.vgr + - name: fail if valgrind failed + run: /bin/false + if: steps.valgrind.outcome != 'success' + - name: clean-nonroot-artifacts + run: rm -f tests/dmevents.out tests/directio.out + - name: root-test + run: sudo make DIO_TEST_DEV=/dev/ram0 test + - name: kpartx-test + run: sudo make -C kpartx test diff --git a/.github/workflows/codingstyle.yml b/.github/workflows/codingstyle.yml new file mode 100644 index 000000000..a36b3874e --- /dev/null +++ b/.github/workflows/codingstyle.yml @@ -0,0 +1,43 @@ +name: Check coding style + +on: + push: + branches: + - master + - queue + - tip + - 'stable-*' + paths: + - '.clang-format' + - '.github/workflows/codingstyle.yaml' + - '**.h' + - '**.c' + pull_request: + branches: + - master + - queue + - 'stable-*' + paths: + - '.clang-format' + - '.github/workflows/codingstyle.yaml' + - '**.h' + - '**.c' + +permissions: + contents: read + +jobs: + stylecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: >- + echo "BASEREF=${{ github.event.pull_request.base.sha }}" >>$GITHUB_ENV + if: github.event_name == 'pull_request' + - run: >- + echo "BASEREF=${{ github.event.before }}" >>$GITHUB_ENV + if: github.event_name == 'push' + - run: git fetch --depth=1 origin ${{ env.BASEREF }} + - uses: yshui/git-clang-format-lint@master + with: + base: ${{ env.BASEREF }} diff --git a/.github/workflows/coverity.yaml b/.github/workflows/coverity.yaml new file mode 100644 index 000000000..30e5471d3 --- /dev/null +++ b/.github/workflows/coverity.yaml @@ -0,0 +1,52 @@ +name: coverity +on: + push: + branches: + - coverity + +jobs: + upload-coverity-scan: + runs-on: ubuntu-24.04 + steps: + - name: checkout + uses: actions/checkout@v4 + - name: dependencies + run: > + sudo apt-get install --yes + gcc make pkg-config + libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev + libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev + libmount-dev + - name: download coverity + run: > + curl -o cov-analysis-linux64.tar.gz + --form token="$COV_TOKEN" + --form project="$COV_PROJECT" + https://scan.coverity.com/download/cxx/linux64 + env: + COV_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} + COV_PROJECT: ${{ secrets.COVERITY_SCAN_PROJECT }} + - name: unpack coverity + run: | + mkdir -p coverity + tar xfz cov-analysis-linux64.tar.gz --strip 1 -C coverity + - name: build with cov-build + run: > + PATH="$PWD/coverity/bin:$PATH" + cov-build --dir cov-int make -Orecurse -j"$(nproc)" + - name: pack results + run: tar cfz multipath-tools.tgz cov-int + - name: submit results + run: > + curl + --form token="$COV_TOKEN" + --form email="$COV_EMAIL" + --form file="@multipath-tools.tgz" + --form version="${{ github.ref_name }}" + --form description="$(git describe --tags --match "0.*")" + --form project="$COV_PROJECT" + https://scan.coverity.com/builds + env: + COV_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} + COV_PROJECT: ${{ secrets.COVERITY_SCAN_PROJECT }} + COV_EMAIL: ${{ secrets.COVERITY_SCAN_EMAIL }} diff --git a/.github/workflows/foreign.yaml b/.github/workflows/foreign.yaml new file mode 100644 index 000000000..42e21f580 --- /dev/null +++ b/.github/workflows/foreign.yaml @@ -0,0 +1,183 @@ +name: compile and unit test on foreign arch +on: + push: + branches: + - master + - queue + - tip + - 'stable-*' + paths: + - '.github/workflows/foreign.yaml' + - '**.h' + - '**.c' + - '**Makefile*' + - '**.mk' + pull_request: + branches: + - master + - queue + - 'stable-*' + paths: + - '.github/workflows/foreign.yaml' + - '**.h' + - '**.c' + - '**Makefile*' + - '**.mk' + +jobs: + + cross-build: + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + os: [bullseye, bookworm, trixie, sid] + arch: [ppc64le, arm64, s390x] + exclude: + - os: bullseye + arch: ppc64le + - os: bullseye + arch: s390x + container: ghcr.io/mwilck/multipath-cross-debian_cross-${{ matrix.os }}-${{ matrix.arch }} + steps: + - name: checkout + uses: actions/checkout@v4 + - name: build + run: make -j$(nproc) -Orecurse test-progs.tar + - name: upload binary archive + uses: actions/upload-artifact@v4 + with: + name: cross-${{ matrix.os }}-${{ matrix.arch }} + path: test-progs.tar + overwrite: true + + test: + runs-on: ubuntu-24.04 + needs: cross-build + strategy: + fail-fast: false + matrix: + os: [bullseye, bookworm, trixie, sid] + arch: [ppc64le, arm64, s390x] + exclude: + - os: bullseye + arch: ppc64le + - os: bullseye + arch: s390x + steps: + - name: set container arch + run: echo CONTAINER_ARCH="${{ matrix.arch }}" >> $GITHUB_ENV + if: matrix.arch != 'armhf' + - name: set container arch + run: echo CONTAINER_ARCH="arm/v7" >> $GITHUB_ENV + if: matrix.arch == 'armhf' + - name: download binary archive + uses: actions/download-artifact@v4 + with: + name: cross-${{ matrix.os }}-${{ matrix.arch }} + - name: unpack binary archive + run: tar xfv test-progs.tar + - name: enable foreign arch + uses: docker/setup-qemu-action@v2 + with: + image: tonistiigi/binfmt:latest + - name: Set coredump pattern + run: | + echo 'core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern + - name: run tests + id: test + run: | + docker run \ + --pids-limit 4096 \ + --workdir /__w/multipath-tools/multipath-tools \ + --platform linux/${{ env.CONTAINER_ARCH }} \ + -w /__w/multipath-tools/multipath-tools \ + -v${{ github.workspace }}:/__w/multipath-tools/multipath-tools \ + ghcr.io/mwilck/multipath-run-debian-${{ matrix.os }} -C tests + continue-on-error: true + - name: Make core dumps accessible + run: | + find . \( -name 'core*' \) -print0 | xargs -0 -r sudo chmod a+x + if: steps.test.outcome != 'success' + - name: save test outputs + run: make test-outputs.tar + if: steps.test.outcome != 'success' + - name: upload test outputs + uses: actions/upload-artifact@v4 + with: + name: test-${{ matrix.os }}-${{ matrix.arch }} + path: test-outputs.tar + overwrite: true + if: steps.test.outcome != 'success' + - name: fail if test failed + run: /bin/false + if: steps.test.outcome != 'success' + + root-test: + runs-on: ubuntu-24.04 + needs: cross-build + strategy: + fail-fast: false + matrix: + os: [bullseye, bookworm, trixie, sid] + arch: [ppc64le, arm64, s390x] + exclude: + - os: bullseye + arch: ppc64le + - os: bullseye + arch: s390x + steps: + - name: mpath + run: sudo modprobe dm_multipath + - name: brd + run: sudo modprobe brd rd_nr=1 rd_size=65536 + - name: set container arch + run: echo CONTAINER_ARCH="${{ matrix.arch }}" >> $GITHUB_ENV + if: matrix.arch != 'armhf' + - name: set container arch + run: echo CONTAINER_ARCH="arm/v7" >> $GITHUB_ENV + if: matrix.arch == 'armhf' + - name: download binary archive + uses: actions/download-artifact@v4 + with: + name: cross-${{ matrix.os }}-${{ matrix.arch }} + - name: unpack binary archive + run: tar xfv test-progs.tar + - name: enable foreign arch + uses: docker/setup-qemu-action@v2 + with: + image: tonistiigi/binfmt:latest + - name: Set coredump pattern + run: | + echo 'core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern + - name: run tests + id: root-test + run: | + docker run \ + --workdir /__w/multipath-tools/multipath-tools \ + --pids-limit 4096 \ + --platform linux/${{ env.CONTAINER_ARCH }} \ + --privileged \ + -v /dev/ram0:/dev/ram0 -e DIO_TEST_DEV=/dev/ram0 \ + -w /__w/multipath-tools/multipath-tools \ + -v${{ github.workspace }}:/__w/multipath-tools/multipath-tools \ + ghcr.io/mwilck/multipath-run-debian-${{ matrix.os }} \ + -C tests dmevents.out + continue-on-error: true + - name: Make core dumps accessible + run: | + find . \( -name 'core*' \) -print0 | xargs -0 -r sudo chmod a+x + if: steps.root-test.outcome != 'success' + - name: save test outputs + run: make test-outputs.tar + if: steps.root-test.outcome != 'success' + - name: upload test outputs + uses: actions/upload-artifact@v4 + with: + name: root-test-${{ matrix.os }}-${{ matrix.arch }} + path: test-outputs.tar + overwrite: true + if: steps.root-test.outcome != 'success' + - name: fail if root test failed + run: /bin/false + if: steps.root-test.outcome != 'success' diff --git a/.github/workflows/multiarch-stable.yaml b/.github/workflows/multiarch-stable.yaml new file mode 100644 index 000000000..7dbcc8f6d --- /dev/null +++ b/.github/workflows/multiarch-stable.yaml @@ -0,0 +1,98 @@ +name: multiarch test for stable distros +on: + push: + branches: + - master + - queue + - tip + - 'stable-*' + paths: + - '.github/workflows/multiarch-stable.yaml' + - '**.h' + - '**.c' + - '**Makefile*' + - '**.mk' + pull_request: + branches: + - master + - queue + - 'stable-*' + paths: + - '.github/workflows/multiarch-stable.yaml' + - '**.h' + - '**.c' + - '**Makefile*' + - '**.mk' +jobs: + + build-old: + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + os: + - debian-trixie + - debian-bookworm + - debian-buster + - ubuntu-trusty + arch: [386, arm/v7] + include: + - os: debian-trixie + arch: s390x + - os: debian-trixie + arch: ppc64le + - os: debian-bookworm + arch: s390x + - os: debian-bookworm + arch: ppc64le + steps: + - name: checkout + uses: actions/checkout@v4 + - name: enable foreign arch + uses: docker/setup-qemu-action@v2 + with: + image: tonistiigi/binfmt:latest + - name: set architecture name + run: | + __arch="${{ matrix.arch }}" + echo "ARCH_NAME=${__arch//\//-}" >>"$GITHUB_ENV" + - name: Set coredump pattern + run: | + echo 'core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern + - name: compile and run unit tests + id: test + run: | + docker run --pids-limit 4096 --platform linux/${{ matrix.arch }} \ + -w /build -v${{ github.workspace }}:/build \ + ghcr.io/mwilck/multipath-build-${{ matrix.os }} test + continue-on-error: true + - name: create binary archive + run: | + docker run --platform linux/${{ matrix.arch }} \ + -w /build -v${{ github.workspace }}:/build \ + ghcr.io/mwilck/multipath-build-${{ matrix.os }} test-progs.tar + if: steps.test.outcome != 'success' + - name: upload binary archive + uses: actions/upload-artifact@v4 + with: + name: binaries-${{ matrix.os }}-${{ env.ARCH_NAME }} + path: test-progs.tar + overwrite: true + if: steps.test.outcome != 'success' + - name: Make core dumps accessible + run: | + find . \( -name 'core*' \) -print0 | xargs -0 -r sudo chmod a+r + if: steps.test.outcome != 'success' + - name: save test outputs + run: make test-outputs.tar + if: steps.test.outcome != 'success' + - name: upload test outputs + uses: actions/upload-artifact@v4 + with: + name: test-${{ matrix.os }}-${{ env.ARCH_NAME }} + path: test-outputs.tar + overwrite: true + if: steps.test.outcome != 'success' + - name: fail if test failed + run: /bin/false + if: steps.test.outcome != 'success' diff --git a/.github/workflows/multiarch.yaml b/.github/workflows/multiarch.yaml new file mode 100644 index 000000000..3aee3b028 --- /dev/null +++ b/.github/workflows/multiarch.yaml @@ -0,0 +1,113 @@ +name: multiarch test for rolling distros +on: + push: + branches: + - master + - queue + - tip + - 'stable-*' + - musl + paths: + - '.github/workflows/multiarch.yaml' + - '**.h' + - '**.c' + - '**Makefile*' + - '**.mk' + pull_request: + branches: + - master + - queue + - 'stable-*' + paths: + - '.github/workflows/multiarch.yaml' + - '**.h' + - '**.c' + - '**Makefile*' + - '**.mk' + # run monthly to catch rolling distro changes + schedule: + - cron: '45 02 1 * *' + +jobs: + + build-current: + strategy: + fail-fast: false + matrix: + os: + - alpine + - debian-sid + - fedora-rawhide + - opensuse-tumbleweed + arch: [ppc64le, s390x, 386, arm/v7] + variant: + - arch: ppc64le + runner: ubuntu-24.04 + - arch: s390x + runner: ubuntu-24.04 + - arch: 386 + runner: ubuntu-24.04 + - arch: arm/v7 + runner: ubuntu-24.04-arm + exclude: + - os: fedora-rawhide + variant: + arch: 386 + runner: ubuntu-24.04 + - os: fedora-rawhide + variant: + arch: arm/v7 + runner: ubuntu-24.04-arm + include: + - os: alpine + variant: + arch: aarch64 + runner: ubuntu-24.04-arm + runs-on: ${{ matrix.variant.runner }} + steps: + - name: checkout + uses: actions/checkout@v4 + - name: enable foreign arch + uses: docker/setup-qemu-action@v2 + with: + image: tonistiigi/binfmt:latest + - name: set architecture name + run: | + __arch="${{ matrix.variant.arch }}" + echo "ARCH_NAME=${__arch//\//-}" >>"$GITHUB_ENV" + - name: Set coredump pattern + run: | + echo 'core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern + - name: compile and run unit tests + id: test + run: | + docker run --pids-limit 4096 --platform linux/${{ matrix.variant.arch }} \ + -w /build -v${{ github.workspace }}:/build \ + ghcr.io/mwilck/multipath-build-${{ matrix.os }} test + continue-on-error: true + - name: Make core dumps accessible + run: | + find . \( -name 'core*' \) -print0 | xargs -0 -r sudo chmod a+x + if: steps.test.outcome != 'success' + - name: create archives + run: | + docker run --platform linux/${{ matrix.variant.arch }} \ + -w /build -v${{ github.workspace }}:/build \ + ghcr.io/mwilck/multipath-build-${{ matrix.os }} test-progs.tar test-outputs.tar + if: steps.test.outcome != 'success' + - name: upload binary archive + uses: actions/upload-artifact@v4 + with: + name: binaries-${{ matrix.os }}w-${{ env.ARCH_NAME }} + path: test-progs.tar + if: steps.test.outcome != 'success' + - name: upload test outputs + uses: actions/upload-artifact@v4 + with: + name: test-${{ matrix.os }}-${{ env.ARCH_NAME }} + path: test-outputs.tar + overwrite: true + if: steps.test.outcome != 'success' + - name: fail if test failed + run: /bin/false + if: steps.test.outcome != 'success' diff --git a/.github/workflows/native.yaml b/.github/workflows/native.yaml new file mode 100644 index 000000000..9c4fa2e82 --- /dev/null +++ b/.github/workflows/native.yaml @@ -0,0 +1,239 @@ +name: compile and unit test on native arch +on: + push: + branches: + - master + - queue + - tip + - 'stable-*' + paths: + - '.github/workflows/native.yaml' + - '**.h' + - '**.c' + - '**Makefile*' + - '**.mk' + pull_request: + branches: + - master + - queue + - 'stable-*' + paths: + - '.github/workflows/native.yaml' + - '**.h' + - '**.c' + - '**Makefile*' + - '**.mk' + +jobs: + stable: + strategy: + fail-fast: false + matrix: + os: + - debian-jessie + - debian-buster + - debian-bullseye + - debian-bookworm + - debian-trixie + - fedora-43 + - opensuse-leap-15.6 + - opensuse-leap-16.0 + variant: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + runs-on: ${{ matrix.variant.runner }} + steps: + - name: checkout + uses: actions/checkout@v4 + + # On jessie, we use libreadline 5 (no licensing issue) + - name: set readline make option (Jessie) + run: echo READLINE=libreadline >> $GITHUB_ENV + if: matrix.os == 'debian-jessie' + + - name: Set coredump pattern + run: | + echo 'core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern + + - name: build and test + id: test + run: | + docker run -w ${{ github.workspace }} -v${{ github.workspace }}:${{ github.workspace }} \ + ghcr.io/mwilck/multipath-build-${{ matrix.os }} \ + -j$(nproc) -Orecurse V=1 READLINE=$READLINE test + continue-on-error: true + + - name: create test-progs.tar + run: | + docker run -w ${{ github.workspace }} -v${{ github.workspace }}:${{ github.workspace }} \ + ghcr.io/mwilck/multipath-build-${{ matrix.os }} test-progs.tar + + - name: upload test-progs.tar + uses: actions/upload-artifact@v4 + with: + name: native-${{ matrix.os }}-${{ matrix.variant.arch }} + path: test-progs.tar + overwrite: true + + - name: Make core dumps accessible + run: | + find . \( -name 'core*' \) -print0 | xargs -0 -r sudo chmod a+r + if: steps.test.outcome != 'success' + + - name: create test-outputs.tar + run: make test-outputs.tar + if: steps.test.outcome != 'success' + + - name: upload test-outputs.tar + uses: actions/upload-artifact@v4 + with: + name: gcc-outputs-${{ matrix.os }}-${{ matrix.variant.arch }} + path: test-outputs.tar + overwrite: true + if: steps.test.outcome != 'success' + + - name: fail if test failed + run: /bin/false + if: steps.test.outcome != 'success' + + clang: + strategy: + fail-fast: false + matrix: + os: + - debian-buster + - debian-bullseye + - debian-bookworm + - debian-trixie + - fedora-43 + - opensuse-leap-15.6 + - opensuse-leap-16.0 + variant: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + include: + - os: debian-jessie + variant: + arch: x86_64 + runner: ubuntu-24.04 + runs-on: ${{ matrix.variant.runner }} + steps: + - name: checkout + uses: actions/checkout@v4 + + # On jessie, we use libreadline 5 (no licensing issue) + - name: set readline make option (Jessie) + run: echo READLINE=libreadline >> $GITHUB_ENV + if: matrix.os == 'debian-jessie' + + - name: Set coredump pattern + run: | + echo 'core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern + + - name: build and test with clang + id: test + run: | + docker run --pids-limit 4096 -e CC=clang \ + -w ${{ github.workspace }} -v${{ github.workspace }}:${{ github.workspace }} \ + ghcr.io/mwilck/multipath-build-${{ matrix.os }} \ + -j$(nproc) -Orecurse V=1 READLINE=$READLINE test + continue-on-error: true + + - name: Make core dumps accessible + run: | + find . \( -name 'core*' \) -print0 | xargs -0 -r sudo chmod a+r + if: steps.test.outcome != 'success' + + - name: create test-outputs.tar + run: make test-outputs.tar + if: steps.test.outcome != 'success' + + - name: upload test-outputs.tar + uses: actions/upload-artifact@v4 + with: + name: clang-outputs-${{ matrix.os }}-${{ matrix.variant.arch }} + path: test-outputs.tar + overwrite: true + if: steps.test.outcome != 'success' + + - name: fail if test failed + run: /bin/false + if: steps.test.outcome != 'success' + + root-test: + needs: stable + strategy: + fail-fast: false + matrix: + os: + - debian-jessie + - debian-buster + - debian-bullseye + - debian-bookworm + - debian-trixie + - fedora-43 + - opensuse-leap-15.6 + - opensuse-leap-16.0 + variant: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + runs-on: ${{ matrix.variant.runner }} + steps: + - name: mpath + run: sudo modprobe dm_multipath + - name: brd + run: sudo modprobe brd rd_nr=1 rd_size=65536 + + - name: checkout + uses: actions/checkout@v4 + + - name: download binary archive + uses: actions/download-artifact@v4 + with: + name: native-${{ matrix.os }}-${{ matrix.variant.arch }} + + - name: unpack binary archive + run: tar xfmv test-progs.tar + + - name: Set coredump pattern + run: | + echo 'core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern + + - name: run root tests + id: test + run: | + docker run \ + --workdir /__w/multipath-tools/multipath-tools --privileged \ + -v /dev/ram0:/dev/ram0 -e DIO_TEST_DEV=/dev/ram0 \ + -w /__w/multipath-tools/multipath-tools \ + -v${{ github.workspace }}:/__w/multipath-tools/multipath-tools \ + ghcr.io/mwilck/multipath-build-${{ matrix.os }} \ + -C tests V=1 directio.out dmevents.out + continue-on-error: true + + - name: Make core dumps accessible + run: | + find . \( -name 'core*' \) -print0 | xargs -0 -r sudo chmod a+r + if: steps.test.outcome != 'success' + + - name: create test-outputs.tar + run: make test-outputs.tar + if: steps.test.outcome != 'success' + + - name: upload test-outputs.tar + uses: actions/upload-artifact@v4 + with: + name: clang-outputs-${{ matrix.os }}-${{ matrix.variant.arch }} + path: test-outputs.tar + overwrite: true + if: steps.test.outcome != 'success' + + - name: fail if test failed + run: /bin/false + if: steps.test.outcome != 'success' diff --git a/.github/workflows/rolling.yaml b/.github/workflows/rolling.yaml new file mode 100644 index 000000000..9f178f776 --- /dev/null +++ b/.github/workflows/rolling.yaml @@ -0,0 +1,96 @@ +name: compile and unit test on rolling distros +on: + push: + branches: + - master + - queue + - tip + - 'stable-*' + paths: + - '.github/workflows/rolling.yaml' + - '**.h' + - '**.c' + - '**Makefile*' + - '**.mk' + pull_request: + branches: + - master + - queue + - 'stable-*' + paths: + - '.github/workflows/rolling.yaml' + - '**.h' + - '**.c' + - '**Makefile*' + - '**.mk' + # run weekly to catch rolling distro changes + schedule: + - cron: '30 06 * * 1' + +jobs: + rolling: + strategy: + fail-fast: false + matrix: + os: + - debian-sid + - opensuse-tumbleweed + - fedora-rawhide + variant: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + compiler: + - gcc + - clang + include: + - os: alpine + variant: + arch: x86_64 + runner: ubuntu-24.04 + compiler: gcc + - os: alpine + variant: + arch: x86_64 + runner: ubuntu-24.04 + compiler: clang + runs-on: ${{ matrix.variant.runner }} + steps: + - name: checkout + uses: actions/checkout@v4 + - name: Set coredump pattern + run: | + echo 'core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern + - name: build and test + id: test + run: | + docker run -w /build -v${{ github.workspace }}:/build \ + ghcr.io/mwilck/multipath-build-${{ matrix.os }} \ + CC=${{ matrix.compiler }} READLINE=libreadline -j$(nproc) -Orecurse test + continue-on-error: true + - name: create binary archive + run: make test-progs.tar + if: steps.test.outcome != 'success' + - name: upload binary archive + uses: actions/upload-artifact@v4 + with: + name: binaries-${{ matrix.os }}-${{ matrix.variant.arch }}-${{ matrix.compiler }} + path: test-progs.tar + if: steps.test.outcome != 'success' + - name: Make core dumps accessible + run: | + find . \( -name 'core*' \) -print0 | xargs -0 -r sudo chmod a+r + if: steps.test.outcome != 'success' + - name: create test-outputs.tar + run: make test-outputs.tar + if: steps.test.outcome != 'success' + - name: upload test-outputs.tar + uses: actions/upload-artifact@v4 + with: + name: outputs-${{ matrix.os }}-${{ matrix.variant.arch }}-${{ matrix.compiler }} + path: test-outputs.tar + if: steps.test.outcome != 'success' + - name: fail + run: /bin/false + if: steps.test.outcome != 'success' From b420ed1fd1092c801ba53e98546aa04337528164 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Fri, 10 Jul 2026 11:44:42 +0200 Subject: [PATCH 19/50] GitHub workflows: replace actions/checkout@v4 with @v7 actions/checkout@v4 uses the deprecated node 20. Signed-off-by: Martin Wilck --- .github/workflows/abi-stable.yaml | 4 ++-- .github/workflows/abi.yaml | 2 +- .github/workflows/build-and-unittest.yaml | 4 ++-- .github/workflows/coverity.yaml | 2 +- .github/workflows/foreign.yaml | 2 +- .github/workflows/multiarch-stable.yaml | 2 +- .github/workflows/multiarch.yaml | 2 +- .github/workflows/native.yaml | 6 +++--- .github/workflows/rolling.yaml | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/abi-stable.yaml b/.github/workflows/abi-stable.yaml index de8050ab0..b41a00dca 100644 --- a/.github/workflows/abi-stable.yaml +++ b/.github/workflows/abi-stable.yaml @@ -54,7 +54,7 @@ jobs: libmount-dev - name: checkout ${{ env.PARENT_TAG }} if: steps.download_abi.outcome != 'success' - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: ref: ${{ env.PARENT_TAG }} - name: build ABI for ${{ env.PARENT_TAG }} @@ -85,7 +85,7 @@ jobs: run: /bin/false if: env.PARENT_TAG == '' - name: checkout ${{ github.ref }} - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: ref: ${{ github.ref }} - name: download ABI for ${{ env.PARENT_TAG }} diff --git a/.github/workflows/abi.yaml b/.github/workflows/abi.yaml index 1a7d3e5dd..4810737a5 100644 --- a/.github/workflows/abi.yaml +++ b/.github/workflows/abi.yaml @@ -25,7 +25,7 @@ jobs: if: env.ABI_BRANCH == '' run: echo "ABI_BRANCH=master" >> $GITHUB_ENV - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: get reference ABI id: reference continue-on-error: true diff --git a/.github/workflows/build-and-unittest.yaml b/.github/workflows/build-and-unittest.yaml index 616a4de1f..386b01db8 100644 --- a/.github/workflows/build-and-unittest.yaml +++ b/.github/workflows/build-and-unittest.yaml @@ -20,7 +20,7 @@ jobs: rl: ['', 'libreadline', 'libedit'] cc: [ gcc, clang ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: update run: sudo apt-get update - name: dependencies @@ -77,7 +77,7 @@ jobs: rl: ['', 'libreadline', 'libedit'] cc: [ gcc, clang ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: mpath run: sudo modprobe dm_multipath - name: brd diff --git a/.github/workflows/coverity.yaml b/.github/workflows/coverity.yaml index 30e5471d3..2e981f8ae 100644 --- a/.github/workflows/coverity.yaml +++ b/.github/workflows/coverity.yaml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: dependencies run: > sudo apt-get install --yes diff --git a/.github/workflows/foreign.yaml b/.github/workflows/foreign.yaml index 42e21f580..9d0c99de9 100644 --- a/.github/workflows/foreign.yaml +++ b/.github/workflows/foreign.yaml @@ -41,7 +41,7 @@ jobs: container: ghcr.io/mwilck/multipath-cross-debian_cross-${{ matrix.os }}-${{ matrix.arch }} steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: build run: make -j$(nproc) -Orecurse test-progs.tar - name: upload binary archive diff --git a/.github/workflows/multiarch-stable.yaml b/.github/workflows/multiarch-stable.yaml index 7dbcc8f6d..cf1b016a4 100644 --- a/.github/workflows/multiarch-stable.yaml +++ b/.github/workflows/multiarch-stable.yaml @@ -47,7 +47,7 @@ jobs: arch: ppc64le steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: enable foreign arch uses: docker/setup-qemu-action@v2 with: diff --git a/.github/workflows/multiarch.yaml b/.github/workflows/multiarch.yaml index 3aee3b028..c187e9cda 100644 --- a/.github/workflows/multiarch.yaml +++ b/.github/workflows/multiarch.yaml @@ -66,7 +66,7 @@ jobs: runs-on: ${{ matrix.variant.runner }} steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: enable foreign arch uses: docker/setup-qemu-action@v2 with: diff --git a/.github/workflows/native.yaml b/.github/workflows/native.yaml index 9c4fa2e82..218be91cf 100644 --- a/.github/workflows/native.yaml +++ b/.github/workflows/native.yaml @@ -46,7 +46,7 @@ jobs: runs-on: ${{ matrix.variant.runner }} steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 # On jessie, we use libreadline 5 (no licensing issue) - name: set readline make option (Jessie) @@ -123,7 +123,7 @@ jobs: runs-on: ${{ matrix.variant.runner }} steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 # On jessie, we use libreadline 5 (no licensing issue) - name: set readline make option (Jessie) @@ -191,7 +191,7 @@ jobs: run: sudo modprobe brd rd_nr=1 rd_size=65536 - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: download binary archive uses: actions/download-artifact@v4 diff --git a/.github/workflows/rolling.yaml b/.github/workflows/rolling.yaml index 9f178f776..5f650e7a5 100644 --- a/.github/workflows/rolling.yaml +++ b/.github/workflows/rolling.yaml @@ -58,7 +58,7 @@ jobs: runs-on: ${{ matrix.variant.runner }} steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set coredump pattern run: | echo 'core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern From 0c76a5485adeefc0d375823e9b72e6b109657207 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Fri, 10 Jul 2026 11:48:05 +0200 Subject: [PATCH 20/50] GitHub workflows: replace setup-qemu-action@v2 with @v4 setup-qemu-action@v2 uses node 20. Signed-off-by: Martin Wilck --- .github/workflows/foreign.yaml | 4 ++-- .github/workflows/multiarch-stable.yaml | 2 +- .github/workflows/multiarch.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/foreign.yaml b/.github/workflows/foreign.yaml index 9d0c99de9..4f187403b 100644 --- a/.github/workflows/foreign.yaml +++ b/.github/workflows/foreign.yaml @@ -78,7 +78,7 @@ jobs: - name: unpack binary archive run: tar xfv test-progs.tar - name: enable foreign arch - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v4 with: image: tonistiigi/binfmt:latest - name: Set coredump pattern @@ -144,7 +144,7 @@ jobs: - name: unpack binary archive run: tar xfv test-progs.tar - name: enable foreign arch - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v4 with: image: tonistiigi/binfmt:latest - name: Set coredump pattern diff --git a/.github/workflows/multiarch-stable.yaml b/.github/workflows/multiarch-stable.yaml index cf1b016a4..947ea0cab 100644 --- a/.github/workflows/multiarch-stable.yaml +++ b/.github/workflows/multiarch-stable.yaml @@ -49,7 +49,7 @@ jobs: - name: checkout uses: actions/checkout@v7 - name: enable foreign arch - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v4 with: image: tonistiigi/binfmt:latest - name: set architecture name diff --git a/.github/workflows/multiarch.yaml b/.github/workflows/multiarch.yaml index c187e9cda..4f9e68cdb 100644 --- a/.github/workflows/multiarch.yaml +++ b/.github/workflows/multiarch.yaml @@ -68,7 +68,7 @@ jobs: - name: checkout uses: actions/checkout@v7 - name: enable foreign arch - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v4 with: image: tonistiigi/binfmt:latest - name: set architecture name From 2a92d133d906f95f7baf4488794d6a44e471aa8f Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Fri, 10 Jul 2026 12:34:55 +0200 Subject: [PATCH 21/50] Add dependabot.yml for tracking github action updates Use the special branch "workflows" for this purpose. It contains only files related to GitHub workflows. Signed-off-by: Martin Wilck --- dependabot.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 dependabot.yml diff --git a/dependabot.yml b/dependabot.yml new file mode 100644 index 000000000..57dff7fa7 --- /dev/null +++ b/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + # Enable version updates for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + target-branch: "workflows" + commit-message: + prefix: "GitHub workflows [dependabot]: " From 9dfa719f0f91e2c6ef335b0447a3bd8dbdadf7ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 10:59:40 +0000 Subject: [PATCH 22/50] GitHub workflows [dependabot]: Bump actions/upload-artifact from 4 to 7 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/abi-stable.yaml | 4 ++-- .github/workflows/abi.yaml | 4 ++-- .github/workflows/foreign.yaml | 6 +++--- .github/workflows/multiarch-stable.yaml | 4 ++-- .github/workflows/multiarch.yaml | 4 ++-- .github/workflows/native.yaml | 8 ++++---- .github/workflows/rolling.yaml | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/abi-stable.yaml b/.github/workflows/abi-stable.yaml index b41a00dca..61e60d8b6 100644 --- a/.github/workflows/abi-stable.yaml +++ b/.github/workflows/abi-stable.yaml @@ -62,7 +62,7 @@ jobs: run: make -j$(nproc) -Orecurse abi - name: save ABI if: steps.download_abi.outcome != 'success' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: multipath-abi-${{ env.PARENT_TAG }} path: abi @@ -113,7 +113,7 @@ jobs: continue-on-error: true - name: save differences if: steps.check_abi.outcome != 'success' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: abi-test path: abi-test diff --git a/.github/workflows/abi.yaml b/.github/workflows/abi.yaml index 4810737a5..0d679bb4f 100644 --- a/.github/workflows/abi.yaml +++ b/.github/workflows/abi.yaml @@ -47,7 +47,7 @@ jobs: - name: create ABI run: make -Orecurse -j$(nproc) abi.tar.gz - name: save ABI - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: abi path: abi @@ -59,7 +59,7 @@ jobs: run: make abi-test - name: save differences if: steps.compare.outcome == 'failure' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: abi-test path: abi-test diff --git a/.github/workflows/foreign.yaml b/.github/workflows/foreign.yaml index 4f187403b..310fbb650 100644 --- a/.github/workflows/foreign.yaml +++ b/.github/workflows/foreign.yaml @@ -45,7 +45,7 @@ jobs: - name: build run: make -j$(nproc) -Orecurse test-progs.tar - name: upload binary archive - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: cross-${{ matrix.os }}-${{ matrix.arch }} path: test-progs.tar @@ -103,7 +103,7 @@ jobs: run: make test-outputs.tar if: steps.test.outcome != 'success' - name: upload test outputs - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: test-${{ matrix.os }}-${{ matrix.arch }} path: test-outputs.tar @@ -172,7 +172,7 @@ jobs: run: make test-outputs.tar if: steps.root-test.outcome != 'success' - name: upload test outputs - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: root-test-${{ matrix.os }}-${{ matrix.arch }} path: test-outputs.tar diff --git a/.github/workflows/multiarch-stable.yaml b/.github/workflows/multiarch-stable.yaml index 947ea0cab..e69e371c8 100644 --- a/.github/workflows/multiarch-stable.yaml +++ b/.github/workflows/multiarch-stable.yaml @@ -73,7 +73,7 @@ jobs: ghcr.io/mwilck/multipath-build-${{ matrix.os }} test-progs.tar if: steps.test.outcome != 'success' - name: upload binary archive - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: binaries-${{ matrix.os }}-${{ env.ARCH_NAME }} path: test-progs.tar @@ -87,7 +87,7 @@ jobs: run: make test-outputs.tar if: steps.test.outcome != 'success' - name: upload test outputs - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: test-${{ matrix.os }}-${{ env.ARCH_NAME }} path: test-outputs.tar diff --git a/.github/workflows/multiarch.yaml b/.github/workflows/multiarch.yaml index 4f9e68cdb..f0fb4b6a4 100644 --- a/.github/workflows/multiarch.yaml +++ b/.github/workflows/multiarch.yaml @@ -96,13 +96,13 @@ jobs: ghcr.io/mwilck/multipath-build-${{ matrix.os }} test-progs.tar test-outputs.tar if: steps.test.outcome != 'success' - name: upload binary archive - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: binaries-${{ matrix.os }}w-${{ env.ARCH_NAME }} path: test-progs.tar if: steps.test.outcome != 'success' - name: upload test outputs - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: test-${{ matrix.os }}-${{ env.ARCH_NAME }} path: test-outputs.tar diff --git a/.github/workflows/native.yaml b/.github/workflows/native.yaml index 218be91cf..06f830fad 100644 --- a/.github/workflows/native.yaml +++ b/.github/workflows/native.yaml @@ -71,7 +71,7 @@ jobs: ghcr.io/mwilck/multipath-build-${{ matrix.os }} test-progs.tar - name: upload test-progs.tar - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: native-${{ matrix.os }}-${{ matrix.variant.arch }} path: test-progs.tar @@ -87,7 +87,7 @@ jobs: if: steps.test.outcome != 'success' - name: upload test-outputs.tar - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: gcc-outputs-${{ matrix.os }}-${{ matrix.variant.arch }} path: test-outputs.tar @@ -153,7 +153,7 @@ jobs: if: steps.test.outcome != 'success' - name: upload test-outputs.tar - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: clang-outputs-${{ matrix.os }}-${{ matrix.variant.arch }} path: test-outputs.tar @@ -227,7 +227,7 @@ jobs: if: steps.test.outcome != 'success' - name: upload test-outputs.tar - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: clang-outputs-${{ matrix.os }}-${{ matrix.variant.arch }} path: test-outputs.tar diff --git a/.github/workflows/rolling.yaml b/.github/workflows/rolling.yaml index 5f650e7a5..43f5c4c72 100644 --- a/.github/workflows/rolling.yaml +++ b/.github/workflows/rolling.yaml @@ -73,7 +73,7 @@ jobs: run: make test-progs.tar if: steps.test.outcome != 'success' - name: upload binary archive - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: binaries-${{ matrix.os }}-${{ matrix.variant.arch }}-${{ matrix.compiler }} path: test-progs.tar @@ -86,7 +86,7 @@ jobs: run: make test-outputs.tar if: steps.test.outcome != 'success' - name: upload test-outputs.tar - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: outputs-${{ matrix.os }}-${{ matrix.variant.arch }}-${{ matrix.compiler }} path: test-outputs.tar From 73be7841798da3c06a93d41776b332dc9f5978b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 10:59:59 +0000 Subject: [PATCH 23/50] GitHub workflows [dependabot]: Bump actions/checkout from 4 to 7 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/codingstyle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codingstyle.yml b/.github/workflows/codingstyle.yml index a36b3874e..75d9a8a2f 100644 --- a/.github/workflows/codingstyle.yml +++ b/.github/workflows/codingstyle.yml @@ -30,7 +30,7 @@ jobs: stylecheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - run: >- echo "BASEREF=${{ github.event.pull_request.base.sha }}" >>$GITHUB_ENV if: github.event_name == 'pull_request' From 5ad8ce72986ff438822e6f728c152185779d04c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 11:00:08 +0000 Subject: [PATCH 24/50] GitHub workflows [dependabot]: Bump actions/download-artifact Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 8. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v8) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/foreign.yaml | 4 ++-- .github/workflows/native.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/foreign.yaml b/.github/workflows/foreign.yaml index 310fbb650..5e6cf0bfd 100644 --- a/.github/workflows/foreign.yaml +++ b/.github/workflows/foreign.yaml @@ -72,7 +72,7 @@ jobs: run: echo CONTAINER_ARCH="arm/v7" >> $GITHUB_ENV if: matrix.arch == 'armhf' - name: download binary archive - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: cross-${{ matrix.os }}-${{ matrix.arch }} - name: unpack binary archive @@ -138,7 +138,7 @@ jobs: run: echo CONTAINER_ARCH="arm/v7" >> $GITHUB_ENV if: matrix.arch == 'armhf' - name: download binary archive - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: cross-${{ matrix.os }}-${{ matrix.arch }} - name: unpack binary archive diff --git a/.github/workflows/native.yaml b/.github/workflows/native.yaml index 06f830fad..e9a16664a 100644 --- a/.github/workflows/native.yaml +++ b/.github/workflows/native.yaml @@ -194,7 +194,7 @@ jobs: uses: actions/checkout@v7 - name: download binary archive - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: native-${{ matrix.os }}-${{ matrix.variant.arch }} From 7177fa065b7610128981aa27b9ef3d4a7df41653 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 10:59:48 +0000 Subject: [PATCH 25/50] GitHub workflows [dependabot]: Bump dawidd6/action-download-artifact Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 6 to 21. - [Release notes](https://github.com/dawidd6/action-download-artifact/releases) - [Commits](https://github.com/dawidd6/action-download-artifact/compare/v6...v21) --- updated-dependencies: - dependency-name: dawidd6/action-download-artifact dependency-version: '21' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/abi-stable.yaml | 4 ++-- .github/workflows/abi.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/abi-stable.yaml b/.github/workflows/abi-stable.yaml index 61e60d8b6..7d197f49d 100644 --- a/.github/workflows/abi-stable.yaml +++ b/.github/workflows/abi-stable.yaml @@ -33,7 +33,7 @@ jobs: - name: try to download ABI for ${{ env.PARENT_TAG }} id: download_abi continue-on-error: true - uses: dawidd6/action-download-artifact@v6 + uses: dawidd6/action-download-artifact@v21 with: workflow: abi-stable.yaml workflow_conclusion: '' @@ -90,7 +90,7 @@ jobs: ref: ${{ github.ref }} - name: download ABI for ${{ env.PARENT_TAG }} id: download_abi - uses: dawidd6/action-download-artifact@v6 + uses: dawidd6/action-download-artifact@v21 with: workflow: abi-stable.yaml workflow_conclusion: '' diff --git a/.github/workflows/abi.yaml b/.github/workflows/abi.yaml index 0d679bb4f..82af1e498 100644 --- a/.github/workflows/abi.yaml +++ b/.github/workflows/abi.yaml @@ -29,7 +29,7 @@ jobs: - name: get reference ABI id: reference continue-on-error: true - uses: dawidd6/action-download-artifact@v6 + uses: dawidd6/action-download-artifact@v21 with: workflow: abi.yaml branch: ${{ env.ABI_BRANCH }} From 9ff07aaf2a92868e46b0e78bcab1233d187b2169 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Mon, 2 Feb 2026 15:57:48 +0100 Subject: [PATCH 26/50] Update NEWS.md --- .github/actions/spelling/expect.txt | 1 + NEWS.md | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 6abf55c81..24214c7e2 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -231,6 +231,7 @@ sys SYSDIR sysfs sysinit +tbd tcp terabytes TESTDEPS diff --git a/NEWS.md b/NEWS.md index d223646c7..5869fbca5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,13 @@ release. These bug fixes will be tracked in stable branches. See [README.md](README.md) for additional information. +## multipath-tools 0.11.5, tbd + +### CI + +* Updated the `test_kpartx` test script, and added it to the + `basic-build-and-ci` workflow. Backport from 0.14.2. + ## multipath-tools 0.11.4, 2026/01 ### Bug fixes From 77f5d25a01fb71b316738f99056717c1019b76e4 Mon Sep 17 00:00:00 2001 From: Xose Vazquez Perez Date: Mon, 16 Feb 2026 12:25:37 +0100 Subject: [PATCH 27/50] multipath-tools: add missing colon to the multipath output Cc: Martin Wilck Cc: Benjamin Marzinski Cc: Christophe Varoqui Cc: DM_DEVEL-ML Signed-off-by: Xose Vazquez Perez Reviewed-by: Martin Wilck (cherry picked from commit baffc26c8f07856e14a64390b7e7564e5b4bac4f) --- libmultipath/propsel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 664e62fca..6b2fdf64b 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -640,7 +640,7 @@ int select_checker_timeout(struct config *conf, struct path *pp) } pp_set_default(checker_timeout, DEF_TIMEOUT); out: - condlog(3, "%s checker timeout = %u s %s", pp->dev, pp->checker_timeout, + condlog(3, "%s: checker timeout = %u s %s", pp->dev, pp->checker_timeout, origin); return 0; } From cd002e1738f16b9b5a23ab0a5e6842a6c68a8b1c Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Mon, 13 Apr 2026 18:38:35 -0400 Subject: [PATCH 28/50] libmultipath: Handle SCSI-2 style vpd page 0x83 descriptors Some older SCSI devices return a SCSI-2 style vpd page 0x83, instead of a SPC-2/3 format one. The SCSI-2 page 83 format returns an IEEE WWN in binary encoded hexi-decimal in the 16 bytes following the initial 4-byte page 83 reply header. Check the 7th byte of the vpd page 83 buffer to determine whether this is a SCSI-2 or SPC-2/3 confomant one. Byte 7 is the 3rd byte of first Identification descriptor in a SPC-2/3 confromant vpd page 83. This is a reserved field, and is guaranteed to be 0. If it is not zero, then it is likely the 3rd byte of a SCSI-2 Identifier (The first 3 bytes of the ID are the Organizationally Unique Identifier). Both the sg_inq and scsi_id commands handle vpd page 83 this way. To make sure that the WWID which multipath reads directly from the device matches, it should handle this format as well. Signed-off-by: Benjamin Marzinski Reviewed-by: Martin Wilck (cherry picked from commit ed6f6aef4fd7f763beb2cb43d509c4d191d973f2) --- libmultipath/discovery.c | 13 +++++++++ tests/vpd.c | 58 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index abf84486e..9e0264c09 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -1193,6 +1193,18 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, if (out_len <= 1) return 0; + /* + * Not a valid SPC-2/3 vpd page 83. Assume it's a SCSI-2 style + * descriptor. + */ + if (in_len >= 20 && in[6] != 0) { + len = 0; + vpd_type = 0x3; + vpd_len = in_len - 4; + vpd = in + 4; + goto decode; + } + d = in + 4; while (d <= in + in_len - 4) { bool invalid = false; @@ -1308,6 +1320,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, vpd_type = vpd[1] & 0xf; vpd_len = vpd[3]; vpd += 4; +decode: /* untaint vpd_len for coverity */ if (vpd_len > WWID_SIZE) { condlog(1, "%s: suspicious designator length %zu truncated to %u", diff --git a/tests/vpd.c b/tests/vpd.c index a72865337..41683fe0c 100644 --- a/tests/vpd.c +++ b/tests/vpd.c @@ -331,6 +331,28 @@ static int create_vpd83(unsigned char *buf, size_t bufsiz, const char *id, return n + 4; } +/** + * create_pre_spc3_vpd83() - create pre-SPC3 "device identification" VPD page + * + * @buf, @bufsiz: see above. + * @id: input ID (third byte must be non-zero) + * + * Create a pre-SPC3 "device identification" VPD page. See comments in + * sg3_utils/src/sg_inq.c for details + * + * Return: VPD page length. + */ +static int create_pre_spc3_vpd83(unsigned char *buf, size_t bufsize, + const char *id) +{ + memset(buf, 0, bufsize); + buf[1] = 0x83; + + hex2bin(buf + 4, id, 16, 32); + put_unaligned_be16(16, buf + 2); + return 20; +} + /** * assert_correct_wwid() - test that a retrieved WWID matches expectations * @test: test name @@ -479,6 +501,30 @@ static void test_vpd_str_ ## typ ## _ ## len ## _ ## wlen(void **state) \ test_id, vt->wwid); \ } +/** + * test_vpd_prespc3_WLEN() - test code for pre-SPC3 VPD 83 + * @WLEN: WWID buffer size + */ +#define make_test_vpd_prespc3(wlen) \ +static void test_vpd_prespc3_ ## wlen(void **state) \ +{ \ + struct vpdtest *vt = *state; \ + int n, ret; \ + int exp_len; \ + \ + /* returned size is always uneven */ \ + exp_len = wlen > 33 ? 33 : \ + wlen % 2 == 0 ? wlen - 1 : wlen - 2; \ + \ + n = create_pre_spc3_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), \ + test_id); \ + wrap_will_return(WRAP_IOCTL, n); \ + wrap_will_return(WRAP_IOCTL, vt->vpdbuf); \ + ret = get_vpd_sgio(10, 0x83, 0, vt->wwid, wlen); \ + assert_correct_wwid("test_vpd_prespc3_" #wlen, exp_len, ret, \ + '3', 0, true, test_id, vt->wwid); \ +} + /** * test_vpd_naa_NAA_WLEN() - test code for VPD 83 NAA designation * @NAA: Network Name Authority (2, 3, 5, or 6) @@ -814,6 +860,13 @@ make_test_vpd_str(18, 20, 17) make_test_vpd_str(18, 20, 16) make_test_vpd_str(18, 20, 15) +/* PRE-SPC3, WWID size: 34 */ +make_test_vpd_prespc3(40) +make_test_vpd_prespc3(34) +make_test_vpd_prespc3(33) +make_test_vpd_prespc3(32) +make_test_vpd_prespc3(20) + static int test_vpd(void) { const struct CMUnitTest tests[] = { @@ -945,6 +998,11 @@ static int test_vpd(void) cmocka_unit_test(test_vpd_str_18_20_17), cmocka_unit_test(test_vpd_str_18_20_16), cmocka_unit_test(test_vpd_str_18_20_15), + cmocka_unit_test(test_vpd_prespc3_40), + cmocka_unit_test(test_vpd_prespc3_34), + cmocka_unit_test(test_vpd_prespc3_33), + cmocka_unit_test(test_vpd_prespc3_32), + cmocka_unit_test(test_vpd_prespc3_20), }; return cmocka_run_group_tests(tests, setup, teardown); } From ec67fdd95f88e23d1a922645c0831a7574ec2379 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 19 Mar 2026 16:29:52 +0100 Subject: [PATCH 29/50] multipathd: get_new_state: map PATH_TIMEOUT to PATH_DOWN We map PATH_TIMEOUT to PATH_DOWN in pathinfo(), but not in get_new_state(). Do it there, too, to treat the states consistently. This avoids logging "checker timed out" twice in update_path_state(), even if log_checker_err is set to "once". Signed-off-by: Martin Wilck Reviewed-by: Benjamin Marzinski (cherry picked from commit 8933b22b82c49f2a240193c4dd20105f7f1df26e) --- multipathd/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/multipathd/main.c b/multipathd/main.c index c8f004c95..9156b57f7 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -2441,8 +2441,10 @@ get_new_state(struct path *pp) * Wait for uevent for removed paths; * some LLDDs like zfcp keep paths unavailable * without sending uevents. + * Also, map PATH_TIMEOUT to PATH_DOWN here, like we do in + * pathinfo(). */ - if (newstate == PATH_REMOVED) + if (newstate == PATH_REMOVED || newstate == PATH_TIMEOUT) newstate = PATH_DOWN; if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) { From 2b91ca9f06f222d74fef54f56df30662460beaf1 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 11 Jun 2026 09:25:18 +0200 Subject: [PATCH 30/50] kpartx: Fix integer overflow in GPT size calculation A crafted GPT table with num_partition_entries >= 0x02000000 can trigger an integer overflow in kpartx. Fix it by casting one operand of the multiplication to size_t. Same as kernel commit c5082b70adfe ("partitions/efi: Fix integer overflow in GPT size calculation"). Signed-off-by: Martin Wilck Reported-by: Tristan Reviewed-by: Benjamin Marzinski (cherry picked from commit 15310a33712447fa740d396fddc1e5ddac466c0a) --- kpartx/gpt.c | 49 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/kpartx/gpt.c b/kpartx/gpt.c index fbf6ae222..8a47e5ac3 100644 --- a/kpartx/gpt.c +++ b/kpartx/gpt.c @@ -64,6 +64,12 @@ #define BLKGETSIZE64 _IOR(0x12,114,sizeof(uint64_t)) /* return device size in bytes (u64 *arg) */ #endif +/* + * The max size of a partition table that we will read for calculating the CRC. + * Note that this is much larger than MAX_SLICES. It's 0x02000000 on most systems. + */ +#define MAX_PT_SIZE ((SIZE_MAX <= UINT32_MAX ? SIZE_MAX : UINT32_MAX) / sizeof(gpt_entry)) + struct blkdev_ioctl_param { unsigned int block; size_t content_length; @@ -82,8 +88,7 @@ struct blkdev_ioctl_param { * Note, the EFI Specification, v1.02, has a reference to * Dr. Dobbs Journal, May 1994 (actually it's in May 1992). */ -static inline uint32_t -efi_crc32(const void *buf, unsigned long len) +static inline uint32_t efi_crc32(const void *buf, size_t len) { return (crc32(~0L, buf, len) ^ ~0L); } @@ -238,10 +243,15 @@ static gpt_entry * alloc_read_gpt_entries(int fd, gpt_header * gpt) { gpt_entry *pte; - size_t count = __le32_to_cpu(gpt->num_partition_entries) * - __le32_to_cpu(gpt->sizeof_partition_entry); + size_t num_entries = __le32_to_cpu(gpt->num_partition_entries); + uint32_t sizeof_pe = __le32_to_cpu(gpt->sizeof_partition_entry); + size_t count; - if (!count) return NULL; + if (!sizeof_pe || num_entries > MAX_PT_SIZE) + return NULL; + count = num_entries * sizeof_pe; + if (!count) + return NULL; if (aligned_malloc((void **)&pte, get_sector_size(fd), &count)) return NULL; @@ -296,6 +306,8 @@ is_gpt_valid(int fd, uint64_t lba, { int rc = 0; /* default to not valid */ uint32_t crc, origcrc; + size_t num_pe; + uint32_t sizeof_pe; if (!gpt || !ptes) return 0; @@ -353,20 +365,25 @@ is_gpt_valid(int fd, uint64_t lba, } /* Check the GUID Partition Entry Array CRC */ - crc = efi_crc32(*ptes, - __le32_to_cpu((*gpt)->num_partition_entries) * - __le32_to_cpu((*gpt)->sizeof_partition_entry)); - if (crc != __le32_to_cpu((*gpt)->partition_entry_array_crc32)) { - // printf("GUID Partition Entry Array CRC check failed.\n"); - free(*gpt); - *gpt = NULL; - free(*ptes); - *ptes = NULL; - return 0; - } + num_pe = __le32_to_cpu((*gpt)->num_partition_entries); + sizeof_pe = __le32_to_cpu((*gpt)->sizeof_partition_entry); + + if (num_pe > MAX_PT_SIZE) + goto bad_crc; + crc = efi_crc32(*ptes, num_pe * sizeof_pe); + if (crc != __le32_to_cpu((*gpt)->partition_entry_array_crc32)) + goto bad_crc; /* We're done, all's well */ return 1; + +bad_crc: + // printf("GUID Partition Entry Array CRC check failed.\n"); + free(*gpt); + *gpt = NULL; + free(*ptes); + *ptes = NULL; + return 0; } /** * compare_gpts() - Search disk for valid GPT headers and PTEs From 2c61cd5db7e2ff975aa2cab940b542e0b4319048 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 11 Jun 2026 16:36:34 +0200 Subject: [PATCH 31/50] kpartx: use mmap() to implement alloc_read_gpt_entries() A maliciously crafted partition table could cause an unnecessarily large memory allocation. By using mmap(), we can calculate the checksum without needing to allocate space for more than the supported number of partitions. Tested with odd values of MAX_SLICES like 1, 2, 4, 7, 8, 19. Signed-off-by: Martin Wilck Reviewed-by: Benjamin Marzinski (cherry picked from commit 8697ea7e35b85dba8a79c9977a0f86f2d28786e6) --- kpartx/gpt.c | 100 +++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/kpartx/gpt.c b/kpartx/gpt.c index 8a47e5ac3..be3955a3c 100644 --- a/kpartx/gpt.c +++ b/kpartx/gpt.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "crc32.h" #include "kpartx.h" @@ -239,29 +240,48 @@ read_lba(int fd, uint64_t lba, void *buffer, size_t bytes) * Allocates space for PTEs based on information found in @gpt. * Notes: remember to free pte when you're done! */ -static gpt_entry * -alloc_read_gpt_entries(int fd, gpt_header * gpt) +static gpt_entry *alloc_read_gpt_entries(int fd, gpt_header *gpt, unsigned int ns) { - gpt_entry *pte; + gpt_entry *pte = NULL; size_t num_entries = __le32_to_cpu(gpt->num_partition_entries); - uint32_t sizeof_pe = __le32_to_cpu(gpt->sizeof_partition_entry); - size_t count; - - if (!sizeof_pe || num_entries > MAX_PT_SIZE) - return NULL; - count = num_entries * sizeof_pe; - if (!count) + size_t n_alloc = num_entries <= ns ? num_entries : ns; + long pagesz = sysconf(_SC_PAGESIZE); + off_t ofs = __le64_to_cpu(gpt->partition_entry_lba) * get_sector_size(fd); + off_t ofs_aligned = ofs & ~(pagesz - 1); + uint32_t crc; + size_t arr_len, count; + unsigned char *pe_map; + + if (!num_entries || num_entries > MAX_PT_SIZE) return NULL; - if (aligned_malloc((void **)&pte, get_sector_size(fd), &count)) - return NULL; - memset(pte, 0, count); + arr_len = num_entries * sizeof(gpt_entry); + ofs -= ofs_aligned; + pe_map = mmap(NULL, arr_len + ofs, PROT_READ, MAP_SHARED, fd, ofs_aligned); - if (!read_lba(fd, __le64_to_cpu(gpt->partition_entry_lba), pte, - count)) { - free(pte); + if (pe_map == MAP_FAILED) { + fprintf(stderr, "%s: mmap failed: %s\n", __func__, strerror(errno)); return NULL; } + + crc = efi_crc32(pe_map + ofs, arr_len); + if (crc != __le32_to_cpu(gpt->partition_entry_array_crc32)) { + fprintf(stderr, "%s: CRC32 checksum mismatch", __func__); + goto out; + } + + count = n_alloc * sizeof(gpt_entry); + pte = malloc(count); + if (pte == NULL) + goto out; + + memcpy(pte, pe_map + ofs, count); + if (n_alloc < num_entries) + fprintf(stderr, + "%s: partition table size (%zu) is larger than supported (%zu)\n", + __func__, num_entries, n_alloc); +out: + munmap(pe_map, arr_len + ofs); return pte; } @@ -300,14 +320,11 @@ alloc_read_gpt_header(int fd, uint64_t lba) * Description: returns 1 if valid, 0 on error. * If valid, returns pointers to newly allocated GPT header and PTEs. */ -static int -is_gpt_valid(int fd, uint64_t lba, - gpt_header ** gpt, gpt_entry ** ptes) +static int is_gpt_valid(int fd, uint64_t lba, gpt_header **gpt, + gpt_entry **ptes, unsigned int ns) { int rc = 0; /* default to not valid */ uint32_t crc, origcrc; - size_t num_pe; - uint32_t sizeof_pe; if (!gpt || !ptes) return 0; @@ -358,33 +375,16 @@ is_gpt_valid(int fd, uint64_t lba, return 0; } - if (!(*ptes = alloc_read_gpt_entries(fd, *gpt))) { + if (!(*ptes = alloc_read_gpt_entries(fd, *gpt, ns))) { free(*gpt); *gpt = NULL; return 0; } - /* Check the GUID Partition Entry Array CRC */ - num_pe = __le32_to_cpu((*gpt)->num_partition_entries); - sizeof_pe = __le32_to_cpu((*gpt)->sizeof_partition_entry); - - if (num_pe > MAX_PT_SIZE) - goto bad_crc; - crc = efi_crc32(*ptes, num_pe * sizeof_pe); - if (crc != __le32_to_cpu((*gpt)->partition_entry_array_crc32)) - goto bad_crc; - /* We're done, all's well */ return 1; - -bad_crc: - // printf("GUID Partition Entry Array CRC check failed.\n"); - free(*gpt); - *gpt = NULL; - free(*ptes); - *ptes = NULL; - return 0; } + /** * compare_gpts() - Search disk for valid GPT headers and PTEs * @pgpt is the primary GPT header @@ -506,8 +506,7 @@ compare_gpts(gpt_header *pgpt, gpt_header *agpt, uint64_t lastlba) * Validity depends on finding either the Primary GPT header and PTEs valid, * or the Alternate GPT header and PTEs valid, and the PMBR valid. */ -static int -find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes) +static int find_valid_gpt(int fd, gpt_header **gpt, gpt_entry **ptes, unsigned int ns) { int good_pgpt = 0, good_agpt = 0, good_pmbr = 0; gpt_header *pgpt = NULL, *agpt = NULL; @@ -520,20 +519,17 @@ find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes) if (!(lastlba = last_lba(fd))) return 0; - good_pgpt = is_gpt_valid(fd, GPT_PRIMARY_PARTITION_TABLE_LBA, - &pgpt, &pptes); + good_pgpt = is_gpt_valid(fd, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, + &pptes, ns); if (good_pgpt) { - good_agpt = is_gpt_valid(fd, - __le64_to_cpu(pgpt->alternate_lba), - &agpt, &aptes); + good_agpt = is_gpt_valid(fd, __le64_to_cpu(pgpt->alternate_lba), + &agpt, &aptes, ns); if (!good_agpt) { - good_agpt = is_gpt_valid(fd, lastlba, - &agpt, &aptes); + good_agpt = is_gpt_valid(fd, lastlba, &agpt, &aptes, ns); } } else { - good_agpt = is_gpt_valid(fd, lastlba, - &agpt, &aptes); + good_agpt = is_gpt_valid(fd, lastlba, &agpt, &aptes, ns); } /* The obviously unsuccessful case */ @@ -626,7 +622,7 @@ read_gpt_pt (int fd, __attribute__((unused)) struct slice all, int last_used_index = -1; int sector_size_mul = get_sector_size(fd)/512; - if (!find_valid_gpt (fd, &gpt, &ptes) || !gpt || !ptes) { + if (!find_valid_gpt(fd, &gpt, &ptes, ns) || !gpt || !ptes) { if (gpt) free (gpt); if (ptes) From 7f268f7c4e9fd2a4ba58524fbe432ed6ade37417 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 11 Jun 2026 17:23:20 +0200 Subject: [PATCH 32/50] kpartx: dasd: avoid out-of-bounds write in read_dasd_pt() Make sure not to write to the struct slice array sp beyond the upper bound (ns). Signed-off-by: Martin Wilck Reported-by: Tristan Reviewed-by: Benjamin Marzinski (cherry picked from commit d08eefd5150470547d6cc38db597dfa5ede80b01) --- kpartx/dasd.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kpartx/dasd.c b/kpartx/dasd.c index f03986455..929b00510 100644 --- a/kpartx/dasd.c +++ b/kpartx/dasd.c @@ -65,9 +65,8 @@ typedef unsigned int __attribute__((__may_alias__)) label_ints_t; /* */ -int -read_dasd_pt(int fd, __attribute__((unused)) struct slice all, - struct slice *sp, __attribute__((unused)) unsigned int ns) +int read_dasd_pt(int fd, __attribute__((unused)) struct slice all, + struct slice *sp, unsigned int ns) { int retval = -1; int blocksize; @@ -255,6 +254,8 @@ read_dasd_pt(int fd, __attribute__((unused)) struct slice all, sp[counter].start = sectors512(offset, blocksize); sp[counter].size = sectors512(size, blocksize); counter++; + if ((unsigned int)counter >= ns) + break; blk++; } retval = counter; From bdcde8ab0f2ca8b7c2851f569a49da2f2f13e2ca Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 11 Jun 2026 17:27:09 +0200 Subject: [PATCH 33/50] kpartx: dasd: avoid infinite loop in read_dasd_pt Make sure that the loop terminates if read() encounters EOF and returns 0. Found with the help of Claude Sonnet 4.6. Signed-off-by: Martin Wilck Reviewed-by: Benjamin Marzinski (cherry picked from commit cd77a2be8967c2dfa24a2b8d0142d71008d1ef1b) --- kpartx/dasd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kpartx/dasd.c b/kpartx/dasd.c index 929b00510..4de02fb20 100644 --- a/kpartx/dasd.c +++ b/kpartx/dasd.c @@ -228,7 +228,7 @@ int read_dasd_pt(int fd, __attribute__((unused)) struct slice all, if (lseek(fd_dasd, blk * blocksize, SEEK_SET) == -1) goto out; - while (read(fd_dasd, data, blocksize) != -1) { + while (read(fd_dasd, data, blocksize) > 0) { format1_label_t f1; memcpy(&f1, data, sizeof(format1_label_t)); From a6a57d5e5bc99eb2d4a94a6ae74e7bf9b36139d3 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 11 Jun 2026 17:30:13 +0200 Subject: [PATCH 34/50] kpartx: dasd: double check blocksize bounds after reading it from disk Avoid a crash when a maliciously crafted disk image reports an invalid block size. Allowed blocksizes are between 512 and 4096 bytes [1]. Found withe the help of Claude Sonnet 4.6. [1] https://www.ibm.com/docs/en/zvm/7.4.0?topic=commands-format Signed-off-by: Martin Wilck Reviewed-by: Benjamin Marzinski (cherry picked from commit 6179c2dfb750a269766ab0c211247c3ec82cb7bf) --- kpartx/dasd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kpartx/dasd.c b/kpartx/dasd.c index 4de02fb20..0f18a28bc 100644 --- a/kpartx/dasd.c +++ b/kpartx/dasd.c @@ -204,6 +204,8 @@ int read_dasd_pt(int fd, __attribute__((unused)) struct slice all, label_ints_t *label = (label_ints_t *) &vlabel; blocksize = label[4]; + if (blocksize < 512 || blocksize > 4096) + goto out; if (label[14] != 0) { /* disk is reserved minidisk */ offset = label[14]; From cda35e9cd463578b625091764d191c6e9188c55b Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 11 Jun 2026 17:33:09 +0200 Subject: [PATCH 35/50] kpartx: dasd: avoid negative partition size Make sure that size - sp[0].start does not result in a negative value. Found withe the help of Claude Sonnet 4.6. Signed-off-by: Martin Wilck Reviewed-by: Benjamin Marzinski (cherry picked from commit c616a95e00d145ae6f97ad03cd00918991c2c997) --- kpartx/dasd.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/kpartx/dasd.c b/kpartx/dasd.c index 0f18a28bc..6e5fd8f2a 100644 --- a/kpartx/dasd.c +++ b/kpartx/dasd.c @@ -71,7 +71,7 @@ int read_dasd_pt(int fd, __attribute__((unused)) struct slice all, int retval = -1; int blocksize; uint64_t disksize; - uint64_t offset, size, fmt_size; + uint64_t offset, size, fmt_size, start; dasd_information_t info; struct hd_geometry geo; char type[5] = {0,}; @@ -214,8 +214,11 @@ int read_dasd_pt(int fd, __attribute__((unused)) struct slice all, offset = info.label_block + 1; size = sectors512(label[8], blocksize); } - sp[0].start = sectors512(offset, blocksize); - sp[0].size = size - sp[0].start; + start = sectors512(offset, blocksize); + if (start >= size) + goto out; + sp[0].start = start; + sp[0].size = size - start; retval = 1; } else if ((strncmp(type, "VOL1", 4) == 0) && (!info.FBA_layout) && (!memcmp(info.type, "ECKD",4))) { @@ -288,8 +291,11 @@ int read_dasd_pt(int fd, __attribute__((unused)) struct slice all, } else size = disksize; - sp[0].start = sectors512(info.label_block + 1, blocksize); - sp[0].size = size - sp[0].start; + start = sectors512(info.label_block + 1, blocksize); + if (start >= size) + goto out; + sp[0].start = start; + sp[0].size = size - start; retval = 1; } From 01e0341626761743d15e8527413b594548df1a82 Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Mon, 15 Jun 2026 19:36:11 -0400 Subject: [PATCH 36/50] libmultipath: don't set hwhander for bio based devices The kernel prints an error message when a bio-based multipath device sets a hardware handler, since bio-based devices only support using the already attached hardware handler as-is. Change select_hwhandler() to not set a hardware handler for bio-based devices. Signed-off-by: Benjamin Marzinski Reviewed-by: Martin Wilck (cherry picked from commit b6c7aab40ea83eafd634907794c21b5ba6f68e4d) --- libmultipath/propsel.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 6b2fdf64b..a860bb955 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -582,6 +582,11 @@ int select_hwhandler(struct config *conf, struct multipath *mp) dh_state = &handler[2]; + if (mp->queue_mode == QUEUE_MODE_BIO) { + mp->hwhandler = DEFAULT_HWHANDLER; + origin = "(setting: disabled due to \"queue_mode bio\")"; + goto set; + } /* * TPGS_UNDEF means that ALUA support couldn't determined either way * yet, probably because the path was always down. @@ -622,6 +627,7 @@ int select_hwhandler(struct config *conf, struct multipath *mp) mp->hwhandler = DEFAULT_HWHANDLER; origin = tpgs_origin; } +set: mp->hwhandler = strdup(mp->hwhandler); condlog(3, "%s: hardware_handler = \"%s\" %s", mp->alias, mp->hwhandler, origin); From 23edba2ae2bfb46ec22a8a2768b1b380b1a8a625 Mon Sep 17 00:00:00 2001 From: Xose Vazquez Perez Date: Sun, 31 May 2026 12:38:27 +0200 Subject: [PATCH 37/50] multipath-tools: align ALUA state strings with kernel scsi_sysfs Update ALUA asymmetric access state descriptions in the prioritizer to match the shorter names defined in the SCSI kernel subsystem: $ grep "{ SCSI_ACCESS_STATE_" linux/drivers/scsi/scsi_sysfs.c { SCSI_ACCESS_STATE_OPTIMAL, "active/optimized" }, { SCSI_ACCESS_STATE_ACTIVE, "active/non-optimized" }, { SCSI_ACCESS_STATE_STANDBY, "standby" }, { SCSI_ACCESS_STATE_UNAVAILABLE, "unavailable" }, { SCSI_ACCESS_STATE_LBA, "lba-dependent" }, { SCSI_ACCESS_STATE_OFFLINE, "offline" }, { SCSI_ACCESS_STATE_TRANSITIONING, "transitioning" }, This unifies diagnostic strings across the stack and replaces the misleading "ARRAY BUG: invalid TPGs state!" message with the standard SPC "reserved" designation. These codes are explicitly reserved for future standard definitions rather than being treated as malformed states. Cc: Martin Wilck Cc: Benjamin Marzinski Cc: Christophe Varoqui Cc: DM_DEVEL-ML Signed-off-by: Xose Vazquez Perez Reviewed-by: Martin Wilck (cherry picked from commit fa2eb1268bf66332b859259051308030f78001fc) --- libmultipath/prioritizers/alua.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libmultipath/prioritizers/alua.c b/libmultipath/prioritizers/alua.c index ec68f3702..3aedbda15 100644 --- a/libmultipath/prioritizers/alua.c +++ b/libmultipath/prioritizers/alua.c @@ -26,16 +26,18 @@ #define ALUA_PRIO_TPGS_FAILED 4 #define ALUA_PRIO_NO_INFORMATION 5 +// clang-format off static const char * aas_string[] = { [AAS_OPTIMIZED] = "active/optimized", [AAS_NON_OPTIMIZED] = "active/non-optimized", [AAS_STANDBY] = "standby", [AAS_UNAVAILABLE] = "unavailable", - [AAS_LBA_DEPENDENT] = "logical block dependent", - [AAS_RESERVED] = "ARRAY BUG: invalid TPGs state!", + [AAS_LBA_DEPENDENT] = "lba-dependent", + [AAS_RESERVED] = "reserved", [AAS_OFFLINE] = "offline", - [AAS_TRANSITIONING] = "transitioning between states", + [AAS_TRANSITIONING] = "transitioning", }; +// clang-format on static const char *aas_print_string(int rc) { From 3d26059f51c2f93831adfd4dddac4bc4424c212e Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Mon, 6 Jul 2026 16:44:52 +0200 Subject: [PATCH 38/50] Update NEWS.md --- NEWS.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 5869fbca5..f03f12033 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,11 +9,30 @@ release. These bug fixes will be tracked in stable branches. See [README.md](README.md) for additional information. -## multipath-tools 0.11.5, tbd +## multipath-tools 0.11.5, 2026/07 + +### User-visible changes + +* Fix ALUA asymmetric access state descriptions in multipathd logs, so that + the same terms are used as by the kernel ("lba-dependent", "transitioning"). +* Don't set a hardware handler for bio-based multipath devices. The kernel + rejects this anyway. + +### Bug fixes + +* Fix WWID detection for legacy devices that use the older SCSI-2 VPD page + 0x83 format for their device identifier. +* kpartx: Fix an integer overflow in the GPT partition table size calculation. + A crafted partition table with an extremely large number of partition entries + could trigger the overflow. +* kpartx: Fix several issues in the DASD partition table reader that could be + triggered by a maliciously crafted disk image. +* Fix duplicate "checker timed out" log messages when `log_checker_err` is + set to `once`. ### CI -* Updated the `test_kpartx` test script, and added it to the +* Update the `test_kpartx` test script, and added it to the `basic-build-and-ci` workflow. Backport from 0.14.2. ## multipath-tools 0.11.4, 2026/01 From c1e69750b04ea608c9da88b5377b3ca0a23d511c Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Mon, 6 Jul 2026 20:48:16 -0400 Subject: [PATCH 39/50] libmultipath: make sure sscanf sets a max field width for strings In the iet and datacore prioritizers, multipath was using sscanf to get a string for a 255 byte buffer, without limiting the size of the string. This could result in a buffer overflow, if there was a bad value in multipath.conf. Signed-off-by: Benjamin Marzinski Reviewed-by: Martin Wilck (cherry picked from commit 4611f971c514f0f6a1fdaa1146d8ecd006cc98a7) --- libmultipath/prioritizers/datacore.c | 6 +++--- libmultipath/prioritizers/iet.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libmultipath/prioritizers/datacore.c b/libmultipath/prioritizers/datacore.c index f288306c2..e8a35df2e 100644 --- a/libmultipath/prioritizers/datacore.c +++ b/libmultipath/prioritizers/datacore.c @@ -51,11 +51,11 @@ int datacore_prio (const char *dev, int sg_fd, char * args, return 0; } - if (sscanf(args, "timeout=%i preferredsds=%s", + if (sscanf(args, "timeout=%i preferredsds=%254s", (int *)&timeout_ms, preferredsds) == 2) {} - else if (sscanf(args, "preferredsds=%s timeout=%i", + else if (sscanf(args, "preferredsds=%254s timeout=%i", preferredsds, (int *)&timeout_ms) == 2) {} - else if (sscanf(args, "preferredsds=%s", + else if (sscanf(args, "preferredsds=%254s", preferredsds) == 1) {} else { dc_log(0, "unexpected prio_args format"); diff --git a/libmultipath/prioritizers/iet.c b/libmultipath/prioritizers/iet.c index f5fdd42aa..3aefaa32c 100644 --- a/libmultipath/prioritizers/iet.c +++ b/libmultipath/prioritizers/iet.c @@ -84,7 +84,7 @@ int iet_prio(const char *dev, char * args) return 0; } // check if args format is OK - if (sscanf(args, "preferredip=%s", preferredip) ==1) {} + if (sscanf(args, "preferredip=%254s", preferredip) == 1) {} else { dc_log(0, "unexpected prio_args format"); return 0; From 2c870ac666b9b291cb83322352e8ddb8737cfc69 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 9 Jul 2026 16:26:54 +0200 Subject: [PATCH 40/50] multipath-tools tests: fix code formatting in vpd.c Avoid that "git clang-format" wrongly indents the invocations of the macros make_test_vpd_str() and make_test_vpd_prespc3 in tests/vpd.c, by appending semicolons to the macro invocation as we did for other macros in the same file. Note: The indentation issue could also be fixed by adding these macros to the "StatementMacros:" directive in .clang-format. Signed-off-by: Martin Wilck --- tests/vpd.c | 58 ++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/vpd.c b/tests/vpd.c index 41683fe0c..0018ce7f8 100644 --- a/tests/vpd.c +++ b/tests/vpd.c @@ -825,47 +825,47 @@ make_test_vpd_naa(2, 17); make_test_vpd_naa(2, 16); /* SCSI Name string: EUI64, WWID size: 17 */ -make_test_vpd_str(0, 20, 18) -make_test_vpd_str(0, 20, 17) -make_test_vpd_str(0, 20, 16) -make_test_vpd_str(0, 20, 15) +make_test_vpd_str(0, 20, 18); +make_test_vpd_str(0, 20, 17); +make_test_vpd_str(0, 20, 16); +make_test_vpd_str(0, 20, 15); /* SCSI Name string: EUI64, zero padded, WWID size: 16 */ -make_test_vpd_str(16, 20, 18) -make_test_vpd_str(16, 20, 17) -make_test_vpd_str(16, 20, 16) -make_test_vpd_str(16, 20, 15) +make_test_vpd_str(16, 20, 18); +make_test_vpd_str(16, 20, 17); +make_test_vpd_str(16, 20, 16); +make_test_vpd_str(16, 20, 15); /* SCSI Name string: NAA, WWID size: 17 */ -make_test_vpd_str(1, 20, 18) -make_test_vpd_str(1, 20, 17) -make_test_vpd_str(1, 20, 16) -make_test_vpd_str(1, 20, 15) +make_test_vpd_str(1, 20, 18); +make_test_vpd_str(1, 20, 17); +make_test_vpd_str(1, 20, 16); +make_test_vpd_str(1, 20, 15); /* SCSI Name string: NAA, zero padded, WWID size: 16 */ -make_test_vpd_str(17, 20, 18) -make_test_vpd_str(17, 20, 17) -make_test_vpd_str(17, 20, 16) -make_test_vpd_str(17, 20, 15) +make_test_vpd_str(17, 20, 18); +make_test_vpd_str(17, 20, 17); +make_test_vpd_str(17, 20, 16); +make_test_vpd_str(17, 20, 15); /* SCSI Name string: IQN, WWID size: 17 */ -make_test_vpd_str(2, 20, 18) -make_test_vpd_str(2, 20, 17) -make_test_vpd_str(2, 20, 16) -make_test_vpd_str(2, 20, 15) +make_test_vpd_str(2, 20, 18); +make_test_vpd_str(2, 20, 17); +make_test_vpd_str(2, 20, 16); +make_test_vpd_str(2, 20, 15); /* SCSI Name string: IQN, zero padded, WWID size: 16 */ -make_test_vpd_str(18, 20, 18) -make_test_vpd_str(18, 20, 17) -make_test_vpd_str(18, 20, 16) -make_test_vpd_str(18, 20, 15) +make_test_vpd_str(18, 20, 18); +make_test_vpd_str(18, 20, 17); +make_test_vpd_str(18, 20, 16); +make_test_vpd_str(18, 20, 15); /* PRE-SPC3, WWID size: 34 */ -make_test_vpd_prespc3(40) -make_test_vpd_prespc3(34) -make_test_vpd_prespc3(33) -make_test_vpd_prespc3(32) -make_test_vpd_prespc3(20) +make_test_vpd_prespc3(40); +make_test_vpd_prespc3(34); +make_test_vpd_prespc3(33); +make_test_vpd_prespc3(32); +make_test_vpd_prespc3(20); static int test_vpd(void) { From e7cfa46e6b5de4cb7f427d26a4d58dbaac78f121 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 9 Jul 2026 16:33:28 +0200 Subject: [PATCH 41/50] Fix minor code formatting issues in latest commits With these changes, "git clang-format --commit 0.14.0" doesn't change any code. Signed-off-by: Martin Wilck --- libmultipath/prioritizers/datacore.c | 13 ++++++------- libmultipath/prioritizers/iet.c | 4 ++-- libmultipath/propsel.c | 4 ++-- tests/vpd.c | 3 +-- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/libmultipath/prioritizers/datacore.c b/libmultipath/prioritizers/datacore.c index e8a35df2e..99bbd69a8 100644 --- a/libmultipath/prioritizers/datacore.c +++ b/libmultipath/prioritizers/datacore.c @@ -51,13 +51,12 @@ int datacore_prio (const char *dev, int sg_fd, char * args, return 0; } - if (sscanf(args, "timeout=%i preferredsds=%254s", - (int *)&timeout_ms, preferredsds) == 2) {} - else if (sscanf(args, "preferredsds=%254s timeout=%i", - preferredsds, (int *)&timeout_ms) == 2) {} - else if (sscanf(args, "preferredsds=%254s", - preferredsds) == 1) {} - else { + if (sscanf(args, "timeout=%i preferredsds=%254s", (int *)&timeout_ms, + preferredsds) == 2) { + } else if (sscanf(args, "preferredsds=%254s timeout=%i", preferredsds, + (int *)&timeout_ms) == 2) { + } else if (sscanf(args, "preferredsds=%254s", preferredsds) == 1) { + } else { dc_log(0, "unexpected prio_args format"); return 0; } diff --git a/libmultipath/prioritizers/iet.c b/libmultipath/prioritizers/iet.c index 3aefaa32c..1e7f1e895 100644 --- a/libmultipath/prioritizers/iet.c +++ b/libmultipath/prioritizers/iet.c @@ -84,8 +84,8 @@ int iet_prio(const char *dev, char * args) return 0; } // check if args format is OK - if (sscanf(args, "preferredip=%254s", preferredip) == 1) {} - else { + if (sscanf(args, "preferredip=%254s", preferredip) == 1) { + } else { dc_log(0, "unexpected prio_args format"); return 0; } diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index a860bb955..1cd9187a5 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -646,8 +646,8 @@ int select_checker_timeout(struct config *conf, struct path *pp) } pp_set_default(checker_timeout, DEF_TIMEOUT); out: - condlog(3, "%s: checker timeout = %u s %s", pp->dev, pp->checker_timeout, - origin); + condlog(3, "%s: checker timeout = %u s %s", pp->dev, + pp->checker_timeout, origin); return 0; } diff --git a/tests/vpd.c b/tests/vpd.c index 0018ce7f8..9d0560c91 100644 --- a/tests/vpd.c +++ b/tests/vpd.c @@ -342,8 +342,7 @@ static int create_vpd83(unsigned char *buf, size_t bufsiz, const char *id, * * Return: VPD page length. */ -static int create_pre_spc3_vpd83(unsigned char *buf, size_t bufsize, - const char *id) +static int create_pre_spc3_vpd83(unsigned char *buf, size_t bufsize, const char *id) { memset(buf, 0, bufsize); buf[1] = 0x83; From 5d225bd243811e6b0c105971584c5e04e6dec067 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 9 Jul 2026 17:17:42 +0200 Subject: [PATCH 42/50] Update NEWS.md Signed-off-by: Martin Wilck --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index f03f12033..28fc6567f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -29,6 +29,8 @@ See [README.md](README.md) for additional information. triggered by a maliciously crafted disk image. * Fix duplicate "checker timed out" log messages when `log_checker_err` is set to `once`. +* Avoid potential buffer overflows in the iet and datacore prioritizers. + Commit 4611f97. ### CI From e7c0a4a542d8d498697da0b1e49aef3ad4b619a6 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Fri, 20 Mar 2026 12:23:46 +0100 Subject: [PATCH 43/50] Makefile: add 'test-outputs.tar' target For saving test outputs in GitHub actions. Also add "test-outputs.cpio", although it'll be hardly used. Signed-off-by: Martin Wilck --- Makefile | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index f06f7faa9..8e46ee732 100644 --- a/Makefile +++ b/Makefile @@ -113,6 +113,7 @@ clean: @touch config.mk $(Q)$(MAKE) $(BUILDDIRS:=.clean) tests.clean || true $(Q)$(RM) -r abi abi.tar.gz abi-test config.mk + $(Q)$(RM) test-progs.* test-outputs.* install: $(BUILDDIRS:=.install) uninstall: $(BUILDDIRS:=.uninstall) @@ -131,10 +132,16 @@ TEST-ARTIFACTS := config.mk Makefile.inc \ tests/Makefile tests/*.so* tests/lib/* tests/*-test test-progs.cpio: test-progs - @printf "%s\\n" $(TEST-ARTIFACTS) | cpio -o -H crc >$@ + $(Q)printf "%s\\n" $(TEST-ARTIFACTS) | cpio -o -H crc >$@ test-progs.tar: test-progs - @tar cf $@ $(TEST-ARTIFACTS) + $(Q)tar cf $@ $(TEST-ARTIFACTS) + +test-outputs.cpio: $(wildcard tests/*.out) $(wildcard tests/*.vgr) + $(Q)printf "%s\\n" $^ | cpio -o -H crc >$@ + +test-outputs.tar: $(wildcard tests/*.out) $(wildcard tests/*.vgr) + $(Q)tar cf "$@" $^ || touch $@ .PHONY: TAGS TAGS: From edb6988f909982bb8f60684ff58df8909ef7d276 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 24 Jun 2026 10:17:50 +0200 Subject: [PATCH 44/50] multipath-tools Makefile: collect core dumps in test-outputs.tar Signed-off-by: Martin Wilck --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8e46ee732..a5f30ce24 100644 --- a/Makefile +++ b/Makefile @@ -137,10 +137,10 @@ test-progs.cpio: test-progs test-progs.tar: test-progs $(Q)tar cf $@ $(TEST-ARTIFACTS) -test-outputs.cpio: $(wildcard tests/*.out) $(wildcard tests/*.vgr) +test-outputs.cpio: $(wildcard tests/*.out) $(wildcard tests/*.vgr) $(wildcard tests/core*) $(Q)printf "%s\\n" $^ | cpio -o -H crc >$@ -test-outputs.tar: $(wildcard tests/*.out) $(wildcard tests/*.vgr) +test-outputs.tar: $(wildcard tests/*.out) $(wildcard tests/*.vgr) $(wildcard tests/core*) $(Q)tar cf "$@" $^ || touch $@ .PHONY: TAGS From 6b1e28e665273493ef22382fdcceaf442a2d4fb2 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Thu, 18 Jun 2026 21:54:45 +0200 Subject: [PATCH 45/50] GitHub workflows: remove spelling action The check_spelling action seems to have been compromised. Disabling it. https://github.com/jsoref/2026-06-16-credential-leak/blob/main/README.md Signed-off-by: Martin Wilck --- .github/actions/spelling/expect.txt | 281 -------------------------- .github/actions/spelling/only.txt | 18 -- .github/actions/spelling/patterns.txt | 116 ----------- .github/workflows/spelling.yml | 180 ----------------- 4 files changed, 595 deletions(-) delete mode 100644 .github/actions/spelling/expect.txt delete mode 100644 .github/actions/spelling/only.txt delete mode 100644 .github/actions/spelling/patterns.txt delete mode 100644 .github/workflows/spelling.yml diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt deleted file mode 100644 index 24214c7e2..000000000 --- a/.github/actions/spelling/expect.txt +++ /dev/null @@ -1,281 +0,0 @@ -abi -adt -aio -Alletra -alloc -alltgpt -alua -aptpl -ASAN -ascq -ata -autoconfig -autodetected -autoresize -backported -barbie -BINDIR -bitfield -blkid -bmarzins -Bsymbolic -cciss -CFLAGS -cgroups -christophe -clangd -clariion -cmdline -cmocka -coldplug -commandline -COMPAQ -configdir -configfile -configurator -coverity -cplusplus -CPPFLAGS -ctx -dasd -datacore -ddf -Debian -DESTDIR -devmaps -devname -devnode -devpath -DEVTYPE -DGC -DIO -directio -disablequeueing -dmevent -dmi -dmmp -dmraid -dmsetup -dracut -EAGAIN -ECKD -emc -Engenio -EVPD -Exos -failback -failover -fds -fexceptions -FFFFFFFF -fge -fno -followover -forcequeueing -fpin -fulldescr -gcc -getprkey -getprstatus -getrlimit -getuid -github -gitlab -google -GPT -hbtl -hds -HITACHI -hotplug -HPE -HSG -HSV -Huawei -hwhandler -hwtable -iet -ifdef -ifndef -igroup -img -Infini -Infinidat -inotify -inttypes -ioctls -iscsi -isw -kpartx -LDFLAGS -len -libaio -libc -LIBDEPS -libdevmapper -libdmmp -libedit -libjson -libmpathcmd -libmpathpersist -libmpathutil -libmpathvalid -libmultipath -libreadline -libsystemd -libudev -libudevdir -liburcu -linux -LIO -lld -lpthread -Lun -lvm -lvmteam -Lyve -Marzinski -misdetection -mpath -mpathb -mpathpersist -mpathvalid -msecs -multipathc -multipathd -multipathed -multipathing -multipaths -multiqueue -mwilck -NFINIDAT -NOLOG -nompath -NOSCAN -Nosync -nvme -Nytro -OBJDEPS -oneshot -ontap -OOM -opensvc -OPTFLAGS -paramp -partx -pathgroup -pathlist -petabytes -pgpolicy -pgvec -plugindir -PNR -ppc -preferredip -preferredsds -prefixdir -prgeneration -Primera -prioritizer -prkeys -PROUT -pthreads -QSAN -rdac -rdma -readcap -readdescr -readfd -readkeys -readline -readsector -rebranded -reconfig -redhat -restorequeueing -retrigger -RETRYABLE -rhabarber -rootprefix -rootprefixdir -rpmbuild -rport -rtpi -rtprio -sanitizers -sas -sbp -scsi -SCST -sda -Seagate -setmarginal -setprkey -setprstatus -shm -shu -SIGHUP -softdep -spi -srp -ssa -standalone -statedir -stdarg -stdint -STRERR -strerror -suse -svg -switchgroup -sys -SYSDIR -sysfs -sysinit -tbd -tcp -terabytes -TESTDEPS -testname -tgill -TGTDIR -TIDS -tmo -tpg -transitioning -transportid -trnptid -udev -udevadm -udevd -uevent -uid -unitdir -unsetmarginal -unsetprkey -unsetprstatus -unspec -usb -userdata -userspace -usr -uuid -valgrind -varoqui -versioning -Vess -vgr -VNX -vpd -VSN -wakka -weightedpath -wholedisk -Wilck -wildcards -Wno -workflows -wrt -wwid -wwn -wwnn -wwpn diff --git a/.github/actions/spelling/only.txt b/.github/actions/spelling/only.txt deleted file mode 100644 index a1750d935..000000000 --- a/.github/actions/spelling/only.txt +++ /dev/null @@ -1,18 +0,0 @@ -# Files to check - see on.push.paths in spelling.yml -# public header files -libdmmp\.h -mpath_valid\.h -mpath_cmd\.h -mpath_persist\.h -# udev rules -\.rules -\.rules\.in -# systemd unit files -\.service -\.service\.in -\.socket -# man pages -\.[358] -\.[358]\.in -README\.md -NEWS\.md diff --git a/.github/actions/spelling/patterns.txt b/.github/actions/spelling/patterns.txt deleted file mode 100644 index 55d4742eb..000000000 --- a/.github/actions/spelling/patterns.txt +++ /dev/null @@ -1,116 +0,0 @@ -# https://www.gnu.org/software/groff/manual/groff.html -# man troff content -\\f[BCIPR] -# '/" -\\\([ad]q - -# uuid: -\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b - -#commit -\b[0-9a-f]{7}\b - -# Generic Hex numbers -\b0x[0-9a-f]{4}\b -\b0x[0-9a-f]{8}\b -\b0x[0-9a-f]{16}\b - -# WWNN/WWPN (NAA identifiers) -\b(?:0x)?10[0-9a-f]{14}\b -\b(?:0x|3)?[25][0-9a-f]{15}\b -\b(?:0x|3)?6[0-9a-f]{31}\b - -# iSCSI iqn (approximate regex) -\biqn\.[0-9]{4}-[0-9]{2}(?:[\.-][a-z][a-z0-9]*)*\b - -# identifiers -\bCODESET_UTF8\b -\bdev_loss_tmo\b -\bdmmp_mps\b -\bdmmp_pgs\b -\bdmmp_mpath_kdev_name_get\b -\bfast_io_fail_tmo\b -\blibmp_mapinfo\b -\bLimitRTPRIO=?\b -\bmax_fds\b -\bmissing_uev_wait_timeout\b -\bMPATH_MAX_PARAM_LEN\b -\bMPATH_MX_TIDS\b -\bMPATH_MX_TID_LEN\b -\bMPATH_PRIN_RKEY_SA\b -\bMPATH_PRIN_RRES_SA\b -\bMPATH_PRIN_RCAP_SA\b -\bMPATH_PRIN_RFSTAT_SA\b -\bMPATH_PROUT_REG_SA\b -\bMPATH_PROUT_RES_SA\b -\bMPATH_PROUT_REL_SA\b -\bMPATH_PROUT_CLEAR_SA\b -\bMPATH_PROUT_PREE_SA\b -\bMPATH_PROUT_PREE_AB_SA\b -\bMPATH_PROUT_REG_IGN_SA\b -\bMPATH_PROUT_REG_MOV_SA\b -\bMPATH_LU_SCOPE\b -\bMPATH_PRTPE_WE\b -\bMPATH_PRTPE_EA\b -\bMPATH_PRTPE_WE_RO\b -\bMPATH_PRTPE_EA_RO\b -\bMPATH_PRTPE_WE_AR\b -\bMPATH_PRTPE_EA_AR\b -\bMPATH_PR_SKIP\b -\bMPATH_PR_SUCCESS\b -\bMPATH_PR_SYNTAX_ERROR\b -\bMPATH_PR_SENSE_NOT_READY\b -\bMPATH_PR_SENSE_MEDIUM_ERROR\b -\bMPATH_PR_SENSE_HARDWARE_ERROR\b -\bMPATH_PR_ILLEGAL_REQ\b -\bMPATH_PR_SENSE_UNIT_ATTENTION\b -\bMPATH_PR_SENSE_INVALID_OP\b -\bMPATH_PR_SENSE_ABORTED_COMMAND\b -\bMPATH_PR_NO_SENSE\b -\bMPATH_PR_SENSE_MALFORMED\b -\bMPATH_PR_RESERV_CONFLICT\b -\bMPATH_PR_FILE_ERROR\b -\bMPATH_PR_DMMP_ERROR\b -\bMPATH_PR_THREAD_ERROR\b -\bMPATH_PR_OTHER\b -\bMPATH_F_APTPL_MASK\b -\bMPATH_F_ALL_TG_PT_MASK\b -\bMPATH_F_SPEC_I_PT_MASK\b -\bMPATH_PR_TYPE_MASK\b -\bMPATH_PR_SCOPE_MASK\b -\bMPATH_PROTOCOL_ID_FC\b -\bMPATH_PROTOCOL_ID_ISCSI\b -\bMPATH_PROTOCOL_ID_SAS\b -\bMPATH_WWUI_DEVICE_NAME\b -\bMPATH_WWUI_PORT_IDENTIFIER\b -\bmpath_persistent_reserve_init_vecs\b -\bmpath_persistent_reserve_free_vecs\b -\bmpath_recv_reply\b -\bmpath_recv_reply_len\b -\bmpath_recv_reply_data\b -\bMPATHTEST_VERBOSITY\b -\bprkeys_file\b -\bprout-type\b -\bprin_capdescr\b -\bprin_readresv\b -\bprin_resvdescr\b -\bprout_param_descriptor\b -\brq_servact\b -\bRLIMIT_RTPRIO\b -\bSCHED_RT_PRIO\b -\bssize_t\b -\btrnptid_list\b -\buxsock_timeout\b - -# Other -\bTutf8\b -\bUTF-8\b -\bCLARiiON\b -\bGPLv2\b -\bHBAs\b -\bSANtricity\b -\bVTrak\b -\bXSG1\b - - - diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml deleted file mode 100644 index 7943ebb8e..000000000 --- a/.github/workflows/spelling.yml +++ /dev/null @@ -1,180 +0,0 @@ -name: Check Spelling - -# Comment management is handled through a secondary job, for details see: -# https://github.com/check-spelling/check-spelling/wiki/Feature%3A-Restricted-Permissions -# -# `jobs.comment-push` runs when a push is made to a repository and the `jobs.spelling` job needs to make a comment -# (in odd cases, it might actually run just to collapse a comment, but that's fairly rare) -# it needs `contents: write` in order to add a comment. -# -# `jobs.comment-pr` runs when a pull_request is made to a repository and the `jobs.spelling` job needs to make a comment -# or collapse a comment (in the case where it had previously made a comment and now no longer needs to show a comment) -# it needs `pull-requests: write` in order to manipulate those comments. - -# Updating pull request branches is managed via comment handling. -# For details, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-expect-list -# -# These elements work together to make it happen: -# -# `on.issue_comment` -# This event listens to comments by users asking to update the metadata. -# -# `jobs.update` -# This job runs in response to an issue_comment and will push a new commit -# to update the spelling metadata. -# -# `with.experimental_apply_changes_via_bot` -# Tells the action to support and generate messages that enable it -# to make a commit to update the spelling metadata. -# -# `with.ssh_key` -# In order to trigger workflows when the commit is made, you can provide a -# secret (typically, a write-enabled github deploy key). -# -# For background, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-with-deploy-key - -# Sarif reporting -# -# Access to Sarif reports is generally restricted (by GitHub) to members of the repository. -# -# Requires enabling `security-events: write` -# and configuring the action with `use_sarif: 1` -# -# For information on the feature, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Sarif-output - -# Minimal workflow structure: -# -# on: -# push: -# ... -# pull_request_target: -# ... -# jobs: -# # you only want the spelling job, all others should be omitted -# spelling: -# # remove `security-events: write` and `use_sarif: 1` -# # remove `experimental_apply_changes_via_bot: 1` -# ... otherwise adjust the `with:` as you wish - -on: - push: - branches: - - "**" - tags-ignore: - - "**" - paths: - - '.github/workflows/spelling.yml' - - 'README*' - - 'NEWS.md' - - '**.3' - - '**.5' - - '**.8' - - '**.8' - - '**.in' - - '**.service' - - '**.socket' - - '**.rules' - - '**/libdmmp.h' - - '**/mpath_valid.h' - - '**/mpath_cmd.h' - - '**/mpath_persist.h' - - '.github/actions/spelling/*' - pull_request_target: - branches: - - "**" - types: - - 'opened' - - 'reopened' - - 'synchronize' - -jobs: - spelling: - name: Check Spelling - permissions: - contents: read - pull-requests: read - actions: read - security-events: write - outputs: - followup: ${{ steps.spelling.outputs.followup }} - runs-on: ubuntu-latest - if: ${{ contains(github.event_name, 'pull_request') || github.event_name == 'push' }} - concurrency: - group: spelling-${{ github.event.pull_request.number || github.ref }} - # note: If you use only_check_changed_files, you do not want cancel-in-progress - cancel-in-progress: true - steps: - - name: check-spelling - id: spelling - uses: check-spelling/check-spelling@main - with: - suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }} - checkout: true - spell_check_this: opensvc/multipath-tools@master - post_comment: 0 - use_magic_file: 1 - report-timing: 0 - warnings: bad-regex,binary-file,deprecated-feature,large-file,limited-references,no-newline-at-eof,noisy-file,non-alpha-in-dictionary,token-is-substring,unexpected-line-ending,whitespace-in-dictionary,minified-file,unsupported-configuration,no-files-to-check - experimental_apply_changes_via_bot: 1 - use_sarif: ${{ (!github.event.pull_request || (github.event.pull_request.head.repo.full_name == github.repository)) && 1 }} - extra_dictionary_limit: 20 - extra_dictionaries: - cspell:software-terms/dict/softwareTerms.txt - cspell:cpp/src/stdlib-c.txt - - comment-push: - name: Report (Push) - # If your workflow isn't running on push, you can remove this job - runs-on: ubuntu-latest - needs: spelling - permissions: - contents: write - if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push' - steps: - - name: comment - uses: check-spelling/check-spelling@main - with: - checkout: true - spell_check_this: opensvc/multipath-tools@master - task: ${{ needs.spelling.outputs.followup }} - - comment-pr: - name: Report (PR) - # If you workflow isn't running on pull_request*, you can remove this job - runs-on: ubuntu-latest - needs: spelling - permissions: - contents: read - pull-requests: write - if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request') - steps: - - name: comment - uses: check-spelling/check-spelling@main - with: - checkout: true - spell_check_this: opensvc/multipath-tools@master - task: ${{ needs.spelling.outputs.followup }} - experimental_apply_changes_via_bot: 1 - - update: - name: Update PR - permissions: - contents: write - pull-requests: write - actions: read - runs-on: ubuntu-latest - if: ${{ - github.event_name == 'issue_comment' && - github.event.issue.pull_request && - contains(github.event.comment.body, '@check-spelling-bot apply') - }} - concurrency: - group: spelling-update-${{ github.event.issue.number }} - cancel-in-progress: false - steps: - - name: apply spelling updates - uses: check-spelling/check-spelling@main - with: - experimental_apply_changes_via_bot: 1 - checkout: true - ssh_key: "${{ secrets.CHECK_SPELLING }}" From bfca0c09edb85d0d81c7a8ebd9a5ed1de6273715 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Fri, 20 Mar 2026 16:08:21 +0100 Subject: [PATCH 46/50] Makefile: include top-level Makefile in test-progs.tar Signed-off-by: Martin Wilck --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index a5f30ce24..b1dac5b70 100644 --- a/Makefile +++ b/Makefile @@ -127,9 +127,9 @@ test: all valgrind-test: all @$(MAKE) -C tests valgrind -TEST-ARTIFACTS := config.mk Makefile.inc \ +TEST-ARTIFACTS := config.mk Makefile Makefile.inc \ $(LIB_BUILDDIRS:%=%/*.so*) $(PLUGIN_BUILDDIRS:%=%/*.so) \ - tests/Makefile tests/*.so* tests/lib/* tests/*-test + tests/Makefile tests/*.so* tests/lib/* tests/*-test test-progs.cpio: test-progs $(Q)printf "%s\\n" $(TEST-ARTIFACTS) | cpio -o -H crc >$@ From 0f2383fd11a7fd2ccd7bb403db6dc7e40963a031 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Mon, 13 Jul 2026 16:20:18 +0200 Subject: [PATCH 47/50] GitHub workflows: disable codingstyle on stable branch Signed-off-by: Martin Wilck --- .github/workflows/codingstyle.yml | 43 ------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 .github/workflows/codingstyle.yml diff --git a/.github/workflows/codingstyle.yml b/.github/workflows/codingstyle.yml deleted file mode 100644 index 75d9a8a2f..000000000 --- a/.github/workflows/codingstyle.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Check coding style - -on: - push: - branches: - - master - - queue - - tip - - 'stable-*' - paths: - - '.clang-format' - - '.github/workflows/codingstyle.yaml' - - '**.h' - - '**.c' - pull_request: - branches: - - master - - queue - - 'stable-*' - paths: - - '.clang-format' - - '.github/workflows/codingstyle.yaml' - - '**.h' - - '**.c' - -permissions: - contents: read - -jobs: - stylecheck: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - run: >- - echo "BASEREF=${{ github.event.pull_request.base.sha }}" >>$GITHUB_ENV - if: github.event_name == 'pull_request' - - run: >- - echo "BASEREF=${{ github.event.before }}" >>$GITHUB_ENV - if: github.event_name == 'push' - - run: git fetch --depth=1 origin ${{ env.BASEREF }} - - uses: yshui/git-clang-format-lint@master - with: - base: ${{ env.BASEREF }} From 558469cc606bea0c5ea78d42e3c1f67c5e0083be Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Mon, 13 Jul 2026 16:15:12 +0200 Subject: [PATCH 48/50] libmultipath: bump version to 0.11.5 Signed-off-by: Martin Wilck --- libmultipath/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libmultipath/version.h b/libmultipath/version.h index 03d838fc1..32e028951 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -20,9 +20,9 @@ #ifndef VERSION_H_INCLUDED #define VERSION_H_INCLUDED -#define VERSION_CODE 0x000B04 +#define VERSION_CODE 0x000B05 /* MMDDYY, in hex */ -#define DATE_CODE 0x01141A +#define DATE_CODE 0x070D1A #define PROG "multipath-tools" From 70c790f5ba5160f90f70e747187d37a13940aa4a Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Mon, 6 Jul 2026 17:44:54 +0200 Subject: [PATCH 49/50] libmultipath: iet prioritizer: obtain PATH_ID from udev Instead of reading /dev/disk/by-id, use the udev PATH_ID property to derive the device path. Signed-off-by: Martin Wilck Tested-by: Arnaldo Viegas de Lima (cherry picked from commit 5cbd51e418b6ab0dc69b3fff4a583863e6055996) --- libmultipath/prioritizers/Makefile | 2 +- libmultipath/prioritizers/iet.c | 97 ++++++++++++++---------------- 2 files changed, 45 insertions(+), 54 deletions(-) diff --git a/libmultipath/prioritizers/Makefile b/libmultipath/prioritizers/Makefile index ff2524c26..78f69ff76 100644 --- a/libmultipath/prioritizers/Makefile +++ b/libmultipath/prioritizers/Makefile @@ -8,7 +8,7 @@ include ../../Makefile.inc CPPFLAGS += -I$(multipathdir) -I$(mpathutildir) CFLAGS += $(LIB_CFLAGS) LDFLAGS += -L$(multipathdir) -L$(mpathutildir) -LIBDEPS = -lmultipath -lmpathutil -lm -lpthread -lrt +LIBDEPS = -lmultipath -lmpathutil -ludev -lm -lpthread -lrt # If you add or remove a prioritizer also update multipath/multipath.conf.5 LIBS = \ diff --git a/libmultipath/prioritizers/iet.c b/libmultipath/prioritizers/iet.c index 1e7f1e895..cddf29c6d 100644 --- a/libmultipath/prioritizers/iet.c +++ b/libmultipath/prioritizers/iet.c @@ -1,10 +1,11 @@ -#include #include +#include #include #include #include #include #include +#include #include "prio.h" #include "debug.h" #include @@ -26,13 +27,14 @@ // by Olivier Lambert // -#define dc_log(prio, msg) condlog(prio, "%s: iet prio: " msg, dev) +#define dc_log(prio, msg) condlog(prio, "%s: iet prio: " msg, sysname) + // // name: find_regex // @param string: string you want to search into // @param regex: the pattern used // @return result: string found in string with regex, "none" if none -char *find_regex(char * string, char * regex) +char *find_regex(const char *string, const char *regex) { int err; regex_t preg; @@ -73,75 +75,64 @@ char *find_regex(char * string, char * regex) // name: inet_prio // @param // @return prio -int iet_prio(const char *dev, char * args) +int iet_prio(struct udev_device *udev, char *args) { char preferredip_buff[255] = ""; char *preferredip = &preferredip_buff[0]; + const char *by_path; + char *ip; + static bool arg_logged = false; + int prio = 10; + const char *sysname; + + if (!udev) + return 0; + sysname = udev_device_get_sysname(udev); + if (!sysname) + sysname = "UNKNOWN"; + // Phase 1 : checks. If anyone fails, return prio 0. // check if args exists if (!args) { - dc_log(0, "need prio_args with preferredip set"); + if (!arg_logged) { + dc_log(2, "need prio_args with preferredip set"); + arg_logged = true; + } return 0; } // check if args format is OK - if (sscanf(args, "preferredip=%254s", preferredip) == 1) { - } else { - dc_log(0, "unexpected prio_args format"); + if (sscanf(args, "preferredip=%254s", preferredip) != 1) { + if (!arg_logged) { + dc_log(2, "unexpected prio_args format"); + arg_logged = true; + } return 0; } // check if ip is not too short if (strlen(preferredip) <= 7) { - dc_log(0, "prio args: preferredip too short"); + if (!arg_logged) { + dc_log(2, "prio args: preferredip too short"); + arg_logged = true; + } return 0; } - // Phase 2 : find device in /dev/disk/by-path to match device/ip - DIR *dir_p; - struct dirent *dir_entry_p; - enum { BUFFERSIZE = 1024 }; - char buffer[BUFFERSIZE]; - char fullpath[BUFFERSIZE] = "/dev/disk/by-path/"; - dir_p = opendir(fullpath); - if (!dir_p) - goto out; - // loop to find device in /dev/disk/by-path - while( NULL != (dir_entry_p = readdir(dir_p))) { - if (dir_entry_p->d_name[0] != '.') { - char path[BUFFERSIZE] = "/dev/disk/by-path/"; - strcat(path,dir_entry_p->d_name); - ssize_t nchars = readlink(path, buffer, sizeof(buffer)-1); - if (nchars != -1) { - char *device; - buffer[nchars] = '\0'; - device = find_regex(buffer,"(sd[a-z]+)"); - // if device parsed is the right one - if (device!=NULL && strncmp(device, dev, strlen(device)) == 0) { - char *ip; - ip = find_regex(dir_entry_p->d_name,"([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})"); - // if prefferedip and ip fetched matches - if (ip!=NULL && strncmp(ip, preferredip, strlen(ip)) == 0) { - // high prio - free(ip); - free(device); - closedir(dir_p); - return 20; - } - free(ip); - } - free(device); - } - else { - printf("error\n"); - } - } + by_path = udev_device_get_property_value(udev, "ID_PATH"); + if (by_path == NULL) { + dc_log(2, "failed to get BY_PATH property"); + return 0; } - // nothing found, low prio - closedir(dir_p); -out: - return 10; + condlog(3, "%s: iet prio: by_path=%s", sysname, by_path); + ip = find_regex(by_path, + "([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})"); + if (ip!=NULL && strncmp(ip, preferredip, strlen(ip)) == 0) + prio = 20; + + free(ip); + return prio; } int getprio(struct path * pp, char * args) { - return iet_prio(pp->dev, args); + return iet_prio(pp->udev, args); } From f4bbb77df53eaeaf0e8bad79aad938a089ca0061 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Mon, 13 Jul 2026 15:57:05 +0000 Subject: [PATCH 50/50] Update NEWS.md Signed-off-by: Martin Wilck --- NEWS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS.md b/NEWS.md index 28fc6567f..c1b971437 100644 --- a/NEWS.md +++ b/NEWS.md @@ -31,6 +31,9 @@ See [README.md](README.md) for additional information. set to `once`. * Avoid potential buffer overflows in the iet and datacore prioritizers. Commit 4611f97. +* iet prioritizer: avoid misleading error message with systemd 256 and + newer, and properly use udev to derive path parameters. + Fixes [#145](https://github.com/opensvc/multipath-tools/issues/145). ### CI