From 86005aa4ee982fff4c366edb2efaa2d78121880d 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 01accbc8619934fe4167a4ae070b9f3fa91411c1 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 98e4a4d298947bd9abd40c5260ed60da763b1c96 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 8756bea93d2925ffc415d891f705a43e1ecbf8c5 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 af60d1ebbd3204462e9fabb20af6b6e7773819c7 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 68a6d42bd04aba978283c0ecefc9e324df2195ec 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 d1702b3657a8873d8711063338ee9f618012a0fd 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 d078271ec09629ba0ea1fc6482b9d2ee0db54517 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 a3af9d52e35c9fceda6e4c55de8dde4d654b3cd6 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 3b1910c7009203ac45e45e6fcf06990b1211fee0 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 c1ffdc2283a1597b60f53897ff00f674907499e0 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 73e3a74f741739ba416f5d5318a739ed7f91867b 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 94a3c10ab06d56afd8fc591c36d99b58ed22ca04 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 ca12d624d3b8a711566080fdb748d6dd038717ac 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 72ed09c759402cc092907ce373f172ca9c08ea91 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 285a6b57590722eecbad0d27a1f6af169373dc33 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 8ca9c1cb3e18d81403e90d78b3b99a480d090e19 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 88d657166f3cad0133c8e8e950269d6478e96d98 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 | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 481df933d..1e90c5132 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -230,6 +230,7 @@ sys SYSDIR sysfs sysinit +tbd tcp terabytes TESTDEPS diff --git a/NEWS.md b/NEWS.md index 19dccf380..559e594f6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,14 @@ release. These bug fixes will be tracked in stable branches. See [README.md](README.md) for additional information. +## multipath-tools 0.12.3, 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.12.2, 2026/01 ### Bug fixes From 137e95e52f949604b8ce658cb544f3f050a53308 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 4c0fbcf3f..93b68400c 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 ee474dbdd63a6bc82a1caa66e338f79bfa029c84 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 4da8ea4a9..ef3229e43 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -1199,6 +1199,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; @@ -1314,6 +1326,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 2d117252a47c8667dfec7c5f2a9a8d0877e57663 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 efb3df9f3..960189716 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -2472,8 +2472,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 57095f4aea463327cbd0fa39083e89ae59e4f902 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 69ddce99a..df5c42eaa 100644 --- a/kpartx/gpt.c +++ b/kpartx/gpt.c @@ -52,6 +52,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; @@ -70,8 +76,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); } @@ -226,10 +231,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; @@ -284,6 +294,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; @@ -341,20 +353,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 261f6176e5cdff38c0fdd66c98ed64922a543f33 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 df5c42eaa..1068da2e3 100644 --- a/kpartx/gpt.c +++ b/kpartx/gpt.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "crc32.h" #include "kpartx.h" @@ -227,29 +228,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; } @@ -288,14 +308,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; @@ -346,33 +363,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 @@ -494,8 +494,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; @@ -508,20 +507,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 */ @@ -614,7 +610,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 85f8fc874c25ffdc0486d41693b1c69135b7cbf7 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 337d5f45c..a044ec638 100644 --- a/kpartx/dasd.c +++ b/kpartx/dasd.c @@ -53,9 +53,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; @@ -243,6 +242,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 36adc868303e424a6ca51f000ee7f14ca9f6b98c 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 a044ec638..7d8a14ccd 100644 --- a/kpartx/dasd.c +++ b/kpartx/dasd.c @@ -216,7 +216,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 547a754fa6d0631481096013bb1a6c58d1b1cb60 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 7d8a14ccd..108e2303f 100644 --- a/kpartx/dasd.c +++ b/kpartx/dasd.c @@ -192,6 +192,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 94690a4c4e02d5f45afdd7f5e84a1b33d031a5e5 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 108e2303f..9062e9041 100644 --- a/kpartx/dasd.c +++ b/kpartx/dasd.c @@ -59,7 +59,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,}; @@ -202,8 +202,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))) { @@ -276,8 +279,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 02cd3f60561eb42c1088347a1c332bbf0be8ab15 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 93b68400c..5caa60d49 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 a3ca7bc88deb90c4faf441af5fca867268678e7b 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 faf46821e87a8fffe82b98856e4515717a2ab129 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 | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 559e594f6..f178c27b6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,14 +9,32 @@ release. These bug fixes will be tracked in stable branches. See [README.md](README.md) for additional information. -## multipath-tools 0.12.3, tbd +## multipath-tools 0.12.3, 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.12.2, 2026/01 ### Bug fixes From f8fd632f79bdb88aef5ad68257224dbf1749572b 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 ab813a0ea..a7c2ae3e8 100644 --- a/libmultipath/prioritizers/datacore.c +++ b/libmultipath/prioritizers/datacore.c @@ -49,11 +49,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 013ac33c69c0b02cd1504716fab37bf53ea33cd6 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 e7027984b8135d2bf4ac5207588b84b09654bb72 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 a7c2ae3e8..b03d14731 100644 --- a/libmultipath/prioritizers/datacore.c +++ b/libmultipath/prioritizers/datacore.c @@ -49,13 +49,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 5caa60d49..73a88fa48 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 562378f46c9d5d4bd9768f7834572a65e3b65939 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 f178c27b6..a7c4151ff 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 8c2e6322fa40b5fc8414da40f017e7452d006519 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 5b1079263d40b52fcdb9d16b576d7706cbabe5fb 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 2d90834c17cb7c8105b80da9a5b7009bf852d192 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 | 280 -------------------------- .github/actions/spelling/only.txt | 18 -- .github/actions/spelling/patterns.txt | 116 ----------- .github/workflows/spelling.yml | 180 ----------------- 4 files changed, 594 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 1e90c5132..000000000 --- a/.github/actions/spelling/expect.txt +++ /dev/null @@ -1,280 +0,0 @@ -abi -adt -aio -Alletra -alloc -alltgpt -alua -aptpl -ASAN -ascq -ata -autoconfig -autodetected -autoresize -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 b9be9843350130acf7b6bedefa384507bcba51fe 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 405b5c6a13d85d7902b7c3e8e72a1852ff399af3 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 6f17afa3bd35bb146fce61e45b63095ed6859741 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Mon, 13 Jul 2026 16:13:48 +0200 Subject: [PATCH 48/50] libmultipath: bump version to 0.12.3 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 15f5556d2..e46b66de3 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -11,9 +11,9 @@ #ifndef VERSION_H_INCLUDED #define VERSION_H_INCLUDED -#define VERSION_CODE 0x000C02 +#define VERSION_CODE 0x000C03 /* MMDDYY, in hex */ -#define DATE_CODE 0x01141A +#define DATE_CODE 0x070D1A #define PROG "multipath-tools" From 241f420f5e07867ce8d902c387f86fdb5be2e0d6 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 c32af7ee9908eafe46294ac82df29e7ffb95e138 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Mon, 13 Jul 2026 15:56:50 +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 a7c4151ff..42644341d 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