diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt deleted file mode 100644 index 6abf55c81..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 -backported -barbie -BINDIR -bitfield -blkid -bmarzins -Bsymbolic -cciss -CFLAGS -cgroups -christophe -clangd -clariion -cmdline -cmocka -coldplug -commandline -COMPAQ -configdir -configfile -configurator -coverity -cplusplus -CPPFLAGS -ctx -dasd -datacore -ddf -Debian -DESTDIR -devmaps -devname -devnode -devpath -DEVTYPE -DGC -DIO -directio -disablequeueing -dmevent -dmi -dmmp -dmraid -dmsetup -dracut -EAGAIN -ECKD -emc -Engenio -EVPD -Exos -failback -failover -fds -fexceptions -FFFFFFFF -fge -fno -followover -forcequeueing -fpin -fulldescr -gcc -getprkey -getprstatus -getrlimit -getuid -github -gitlab -google -GPT -hbtl -hds -HITACHI -hotplug -HPE -HSG -HSV -Huawei -hwhandler -hwtable -iet -ifdef -ifndef -igroup -img -Infini -Infinidat -inotify -inttypes -ioctls -iscsi -isw -kpartx -LDFLAGS -len -libaio -libc -LIBDEPS -libdevmapper -libdmmp -libedit -libjson -libmpathcmd -libmpathpersist -libmpathutil -libmpathvalid -libmultipath -libreadline -libsystemd -libudev -libudevdir -liburcu -linux -LIO -lld -lpthread -Lun -lvm -lvmteam -Lyve -Marzinski -misdetection -mpath -mpathb -mpathpersist -mpathvalid -msecs -multipathc -multipathd -multipathed -multipathing -multipaths -multiqueue -mwilck -NFINIDAT -NOLOG -nompath -NOSCAN -Nosync -nvme -Nytro -OBJDEPS -oneshot -ontap -OOM -opensvc -OPTFLAGS -paramp -partx -pathgroup -pathlist -petabytes -pgpolicy -pgvec -plugindir -PNR -ppc -preferredip -preferredsds -prefixdir -prgeneration -Primera -prioritizer -prkeys -PROUT -pthreads -QSAN -rdac -rdma -readcap -readdescr -readfd -readkeys -readline -readsector -rebranded -reconfig -redhat -restorequeueing -retrigger -RETRYABLE -rhabarber -rootprefix -rootprefixdir -rpmbuild -rport -rtpi -rtprio -sanitizers -sas -sbp -scsi -SCST -sda -Seagate -setmarginal -setprkey -setprstatus -shm -shu -SIGHUP -softdep -spi -srp -ssa -standalone -statedir -stdarg -stdint -STRERR -strerror -suse -svg -switchgroup -sys -SYSDIR -sysfs -sysinit -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/abi-stable.yaml b/.github/workflows/abi-stable.yaml index 8f9a9dbf3..7d197f49d 100644 --- a/.github/workflows/abi-stable.yaml +++ b/.github/workflows/abi-stable.yaml @@ -21,18 +21,31 @@ jobs: 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' }} + 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' }} + if: github.event_name == 'pull_request' - name: assert parent tag run: /bin/false - if: ${{ env.PARENT_TAG == '' }} + 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@v21 + 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 @@ -40,13 +53,16 @@ jobs: libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev libmount-dev - name: checkout ${{ env.PARENT_TAG }} - uses: actions/checkout@v4 + if: steps.download_abi.outcome != 'success' + uses: actions/checkout@v7 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 - uses: actions/upload-artifact@v4 + if: steps.download_abi.outcome != 'success' + uses: actions/upload-artifact@v7 with: name: multipath-abi-${{ env.PARENT_TAG }} path: abi @@ -59,28 +75,31 @@ jobs: 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' }} + 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' }} + if: github.event_name == 'pull_request' - name: assert parent tag run: /bin/false - if: ${{ env.PARENT_TAG == '' }} + 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 }} id: download_abi - uses: actions/download-artifact@v4 + uses: dawidd6/action-download-artifact@v21 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 - if: steps.download_abi.outcome != 'success' - name: dependencies run: > sudo apt-get install --yes gcc @@ -93,8 +112,8 @@ jobs: 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 + if: steps.check_abi.outcome != 'success' + 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 adc2081f1..82af1e498 100644 --- a/.github/workflows/abi.yaml +++ b/.github/workflows/abi.yaml @@ -2,6 +2,7 @@ name: check-abi on: push: branches: + - master - queue - abi paths: @@ -21,14 +22,14 @@ jobs: runs-on: ubuntu-24.04 steps: - name: set ABI branch - if: ${{ env.ABI_BRANCH == '' }} + 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 - uses: dawidd6/action-download-artifact@v6 + uses: dawidd6/action-download-artifact@v21 with: workflow: abi.yaml branch: ${{ env.ABI_BRANCH }} @@ -46,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 @@ -54,11 +55,11 @@ jobs: - name: compare ABI against reference id: compare continue-on-error: true - if: ${{ steps.reference.outcome == 'success' }} + if: steps.reference.outcome == 'success' run: make abi-test - name: save differences - if: ${{ steps.compare.outcome == 'failure' }} - uses: actions/upload-artifact@v4 + if: steps.compare.outcome == 'failure' + uses: actions/upload-artifact@v7 with: name: abi-test path: abi-test diff --git a/.github/workflows/build-and-unittest.yaml b/.github/workflows/build-and-unittest.yaml index 2fb5327d4..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 @@ -43,7 +43,7 @@ jobs: - 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' }} + if: matrix.cc == 'clang' - name: build run: > make -Orecurse -j$(nproc) @@ -67,6 +67,8 @@ jobs: 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: @@ -75,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 @@ -108,3 +110,5 @@ jobs: 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/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 fdcfaac69..5e6cf0bfd 100644 --- a/.github/workflows/foreign.yaml +++ b/.github/workflows/foreign.yaml @@ -41,11 +41,11 @@ 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 - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: cross-${{ matrix.os }}-${{ matrix.arch }} path: test-progs.tar @@ -67,29 +67,51 @@ jobs: steps: - name: set container arch run: echo CONTAINER_ARCH="${{ matrix.arch }}" >> $GITHUB_ENV - if: ${{ matrix.arch != 'armhf' }} + if: matrix.arch != 'armhf' - name: set container arch run: echo CONTAINER_ARCH="arm/v7" >> $GITHUB_ENV - if: ${{ matrix.arch == 'armhf' }} + 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 run: tar xfv test-progs.tar - name: enable foreign arch - uses: dbhi/qus/action@main + uses: docker/setup-qemu-action@v4 + with: + image: tonistiigi/binfmt:latest + - name: Set coredump pattern + run: | + echo 'core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern - name: run tests - uses: mosteo-actions/docker-run@v1 + 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@v7 with: - image: ghcr.io/mwilck/multipath-run-debian-${{ matrix.os }} - guest-dir: /__w/multipath-tools/multipath-tools - host-dir: ${{ github.workspace }} - command: -C tests - params: > - --workdir /__w/multipath-tools/multipath-tools - --platform linux/${{ env.CONTAINER_ARCH }} - pull-params: "--platform linux/${{ env.CONTAINER_ARCH }}" + 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 @@ -111,35 +133,51 @@ jobs: 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' }} + if: matrix.arch != 'armhf' - name: set container arch run: echo CONTAINER_ARCH="arm/v7" >> $GITHUB_ENV - if: ${{ matrix.arch == 'armhf' }} + 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 run: tar xfv test-progs.tar - name: enable foreign arch - uses: dbhi/qus/action@main - - name: run tests - uses: mosteo-actions/docker-run@v1 + uses: docker/setup-qemu-action@v4 with: - image: ghcr.io/mwilck/multipath-run-debian-${{ matrix.os }} - guest-dir: /__w/multipath-tools/multipath-tools - host-dir: ${{ github.workspace }} - command: -C tests dmevents.out - params: > - --workdir /__w/multipath-tools/multipath-tools - --platform linux/${{ env.CONTAINER_ARCH }} - --privileged - -v /dev/ram0:/dev/ram0 -e DIO_TEST_DEV=/dev/ram0 - pull-params: "--platform linux/${{ env.CONTAINER_ARCH }}" + 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: show root test output - run: for o in tests/*.out; do echo "===== $o ====="; cat "$o"; done - - name: fail + - 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@v7 + 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 == 'failure' }} + if: steps.root-test.outcome != 'success' diff --git a/.github/workflows/multiarch-stable.yaml b/.github/workflows/multiarch-stable.yaml index b8c770e40..e69e371c8 100644 --- a/.github/workflows/multiarch-stable.yaml +++ b/.github/workflows/multiarch-stable.yaml @@ -47,38 +47,52 @@ jobs: arch: ppc64le steps: - name: checkout - uses: actions/checkout@v4 + 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 + 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 - uses: mosteo-actions/docker-run@v1 - with: - image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - guest-dir: /build - host-dir: ${{ github.workspace }} - command: test - params: "--platform linux/${{ matrix.arch }}" - pull-params: "--platform linux/${{ matrix.arch }}" + 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 - uses: mosteo-actions/docker-run@v1 - with: - image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - guest-dir: /build - host-dir: ${{ github.workspace }} - command: test-progs.tar - params: "--platform linux/${{ matrix.arch }}" - pull-params: "--platform linux/${{ matrix.arch }}" + 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 + uses: actions/upload-artifact@v7 with: - name: binaries-${{ matrix.os }}-${{ matrix.arch }} + 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@v7 + with: + name: test-${{ matrix.os }}-${{ env.ARCH_NAME }} + path: test-outputs.tar + overwrite: true if: steps.test.outcome != 'success' - - name: fail + - 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 index 806275a13..f0fb4b6a4 100644 --- a/.github/workflows/multiarch.yaml +++ b/.github/workflows/multiarch.yaml @@ -6,6 +6,7 @@ on: - queue - tip - 'stable-*' + - musl paths: - '.github/workflows/multiarch.yaml' - '**.h' @@ -30,7 +31,6 @@ on: jobs: build-current: - runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: @@ -40,48 +40,74 @@ jobs: - 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 - arch: 386 + variant: + arch: 386 + runner: ubuntu-24.04 - os: fedora-rawhide - arch: arm/v7 + variant: + arch: arm/v7 + runner: ubuntu-24.04-arm include: - os: alpine - arch: aarch64 + variant: + arch: aarch64 + runner: ubuntu-24.04-arm + 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 + uses: docker/setup-qemu-action@v4 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 - uses: mosteo-actions/docker-run@v1 - with: - image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - guest-dir: /build - host-dir: ${{ github.workspace }} - command: test - params: "--platform linux/${{ matrix.arch }}" - pull-params: "--platform linux/${{ matrix.arch }}" + 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: create binary archive - uses: mosteo-actions/docker-run@v1 - with: - image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - guest-dir: /build - host-dir: ${{ github.workspace }} - command: test-progs.tar - params: "--platform linux/${{ matrix.arch }}" - pull-params: "--platform linux/${{ matrix.arch }}" + - 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 + uses: actions/upload-artifact@v7 with: - name: binaries-${{ matrix.os }}-${{ matrix.arch }} + name: binaries-${{ matrix.os }}w-${{ env.ARCH_NAME }} path: test-progs.tar if: steps.test.outcome != 'success' - - name: fail + - name: upload test outputs + uses: actions/upload-artifact@v7 + 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 index 9e89780b1..e9a16664a 100644 --- a/.github/workflows/native.yaml +++ b/.github/workflows/native.yaml @@ -46,62 +46,58 @@ jobs: runs-on: ${{ matrix.variant.runner }} steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - - name: set archive name - # Leap containers have cpio but not tar - run: echo ARCHIVE_TGT=test-progs.cpio >> $GITHUB_ENV - if: ${{ startswith(matrix.os, 'opensuse-leap-15') }} - - name: set archive name - run: echo ARCHIVE_TGT=test-progs.tar >> $GITHUB_ENV - if: ${{ !startswith(matrix.os, 'opensuse-leap-15') }} + # 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 - if: ${{ matrix.os != 'debian-jessie' }} - uses: mosteo-actions/docker-run@v1 - with: - image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - command: -j$(nproc) -Orecurse 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: build and test (jessie) - id: test_jessie - # On jessie, we use libreadline 5 (no licensing issue) - if: ${{ matrix.os == 'debian-jessie' }} - uses: mosteo-actions/docker-run@v1 - with: - image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - command: -j$(nproc) -Orecurse READLINE=libreadline 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: create ${{ env.ARCHIVE_TGT }} - if: ${{ matrix.os != 'debian-jessie' }} - uses: mosteo-actions/docker-run@v1 + - name: upload test-progs.tar + uses: actions/upload-artifact@v7 with: - image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - command: ${{ env.ARCHIVE_TGT }} - - name: create ${{ env.ARCHIVE_TGT }} (jessie) - if: ${{ matrix.os == 'debian-jessie' }} - uses: mosteo-actions/docker-run@v1 - with: - image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - command: READLINE=libreadline ${{ env.ARCHIVE_TGT }} + 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: upload binary archive - uses: actions/upload-artifact@v4 + - name: create test-outputs.tar + run: make test-outputs.tar + if: steps.test.outcome != 'success' + + - name: upload test-outputs.tar + uses: actions/upload-artifact@v7 with: - name: native-${{ matrix.os }}-${{ matrix.variant.arch }} - path: ${{ env.ARCHIVE_TGT }} + name: gcc-outputs-${{ matrix.os }}-${{ matrix.variant.arch }} + path: test-outputs.tar overwrite: true + if: steps.test.outcome != 'success' - - name: fail + - name: fail if test failed run: /bin/false - if: >- - ${{ ( matrix.os == 'debian-jessie' && - steps.test_jessie.outcome != 'success' ) || - ( matrix.os != 'debian-jessie' && - steps.test.outcome != 'success' ) }} + if: steps.test.outcome != 'success' + clang: strategy: fail-fast: false @@ -127,75 +123,46 @@ 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) + run: echo READLINE=libreadline >> $GITHUB_ENV + if: matrix.os == 'debian-jessie' - - name: set archive name - # Leap containers have cpio but not tar - run: echo ARCHIVE_TGT=test-progs.cpio >> $GITHUB_ENV - if: ${{ startswith(matrix.os, 'opensuse-leap-15') }} - - name: set archive name - run: echo ARCHIVE_TGT=test-progs.tar >> $GITHUB_ENV - if: ${{ !startswith(matrix.os, 'opensuse-leap-15') }} + - name: Set coredump pattern + run: | + echo 'core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern - - name: clang + - name: build and test with clang id: test - if: ${{ matrix.os != 'debian-jessie' }} - uses: mosteo-actions/docker-run@v1 - with: - image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - params: -e CC=clang - command: -j$(nproc) -Orecurse test - continue-on-error: true - - name: clang (jessie) - id: test_jessie - if: ${{ matrix.os == 'debian-jessie' }} - uses: mosteo-actions/docker-run@v1 - with: - image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - params: -e CC=clang - command: -j$(nproc) -Orecurse READLINE=libreadline 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: create ${{ env.ARCHIVE_TGT }} - uses: mosteo-actions/docker-run@v1 - with: - image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - command: ${{ env.ARCHIVE_TGT }} - if: >- - ${{ ( matrix.os == 'debian-jessie' && - steps.test_jessie.outcome != 'success' ) || - ( matrix.os != 'debian-jessie' && - steps.test.outcome != 'success' ) }} - - name: create ${{ env.ARCHIVE_TGT }} (jessie) - uses: mosteo-actions/docker-run@v1 - with: - image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - command: READLINE=libreadline ${{ env.ARCHIVE_TGT }} - if: >- - ${{ ( matrix.os == 'debian-jessie' && - steps.test_jessie.outcome != 'success' ) || - ( matrix.os != 'debian-jessie' && - 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: upload binary archive - uses: actions/upload-artifact@v4 + - name: create test-outputs.tar + run: make test-outputs.tar + if: steps.test.outcome != 'success' + + - name: upload test-outputs.tar + uses: actions/upload-artifact@v7 with: - name: clang-${{ matrix.os }}-${{ matrix.variant.arch }} - path: ${{ env.ARCHIVE_TGT }} + name: clang-outputs-${{ matrix.os }}-${{ matrix.variant.arch }} + path: test-outputs.tar overwrite: true - if: >- - ${{ ( matrix.os == 'debian-jessie' && - steps.test_jessie.outcome != 'success' ) || - ( matrix.os != 'debian-jessie' && - steps.test.outcome != 'success' ) }} + if: steps.test.outcome != 'success' - - name: fail + - name: fail if test failed run: /bin/false - if: >- - ${{ ( matrix.os == 'debian-jessie' && - steps.test_jessie.outcome != 'success' ) || - ( matrix.os != 'debian-jessie' && - steps.test.outcome != 'success' ) }} + if: steps.test.outcome != 'success' root-test: needs: stable @@ -224,33 +191,49 @@ 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 + uses: actions/download-artifact@v8 with: name: native-${{ matrix.os }}-${{ matrix.variant.arch }} - - name: unpack binary archive - run: cpio -idv < test-progs.cpio - if: ${{ startswith(matrix.os, 'opensuse-leap-15') }} + - name: unpack binary archive run: tar xfmv test-progs.tar - if: ${{ !startswith(matrix.os, 'opensuse-leap-15') }} + + - name: Set coredump pattern + run: | + echo 'core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern - name: run root tests - uses: mosteo-actions/docker-run@v1 - with: - image: ghcr.io/mwilck/multipath-build-${{ matrix.os }} - guest-dir: /__w/multipath-tools/multipath-tools - host-dir: ${{ github.workspace }} - params: > - --workdir /__w/multipath-tools/multipath-tools --privileged - -v /dev/ram0:/dev/ram0 -e DIO_TEST_DEV=/dev/ram0 - command: -C tests directio.out dmevents.out - id: root-test + 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: show root test output - run: for o in tests/*.out; do echo "===== $o ====="; cat "$o"; done - - name: fail + + - 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@v7 + 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.root-test.outcome == 'failure' }} + if: steps.test.outcome != 'success' diff --git a/.github/workflows/rolling.yaml b/.github/workflows/rolling.yaml index a4706e52e..43f5c4c72 100644 --- a/.github/workflows/rolling.yaml +++ b/.github/workflows/rolling.yaml @@ -56,23 +56,41 @@ jobs: runner: ubuntu-24.04 compiler: clang runs-on: ${{ matrix.variant.runner }} - container: ghcr.io/mwilck/multipath-build-${{ matrix.os }} 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 - name: build and test id: test - run: make CC=${{ matrix.compiler }} READLINE=libreadline -j$(nproc) -Orecurse 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 + uses: actions/upload-artifact@v7 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@v7 + 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' 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 }}" diff --git a/Makefile b/Makefile index f06f7faa9..b1dac5b70 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) @@ -126,15 +127,21 @@ 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 - @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) $(wildcard tests/core*) + $(Q)printf "%s\\n" $^ | cpio -o -H crc >$@ + +test-outputs.tar: $(wildcard tests/*.out) $(wildcard tests/*.vgr) $(wildcard tests/core*) + $(Q)tar cf "$@" $^ || touch $@ .PHONY: TAGS TAGS: diff --git a/NEWS.md b/NEWS.md index d223646c7..c1b971437 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,37 @@ release. These bug fixes will be tracked in stable branches. See [README.md](README.md) for additional information. +## multipath-tools 0.11.5, 2026/07 + +### User-visible changes + +* Fix ALUA asymmetric access state descriptions in multipathd logs, so that + the same terms are used as by the kernel ("lba-dependent", "transitioning"). +* Don't set a hardware handler for bio-based multipath devices. The kernel + rejects this anyway. + +### Bug fixes + +* Fix WWID detection for legacy devices that use the older SCSI-2 VPD page + 0x83 format for their device identifier. +* kpartx: Fix an integer overflow in the GPT partition table size calculation. + A crafted partition table with an extremely large number of partition entries + could trigger the overflow. +* kpartx: Fix several issues in the DASD partition table reader that could be + triggered by a maliciously crafted disk image. +* Fix duplicate "checker timed out" log messages when `log_checker_err` is + set to `once`. +* 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 + +* Update the `test_kpartx` test script, and added it to the + `basic-build-and-ci` workflow. Backport from 0.14.2. + ## multipath-tools 0.11.4, 2026/01 ### Bug fixes 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]: " diff --git a/kpartx/Makefile b/kpartx/Makefile index 849fd36d0..cdadaeca8 100644 --- a/kpartx/Makefile +++ b/kpartx/Makefile @@ -40,8 +40,14 @@ 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: export KPARTX_DEBUG := 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)) diff --git a/kpartx/dasd.c b/kpartx/dasd.c index f03986455..6e5fd8f2a 100644 --- a/kpartx/dasd.c +++ b/kpartx/dasd.c @@ -65,14 +65,13 @@ 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; 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,}; @@ -205,6 +204,8 @@ 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]; @@ -213,8 +214,11 @@ 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))) { @@ -229,7 +233,7 @@ 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)); @@ -255,6 +259,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; @@ -285,8 +291,11 @@ 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; } diff --git a/kpartx/gpt.c b/kpartx/gpt.c index fbf6ae222..be3955a3c 100644 --- a/kpartx/gpt.c +++ b/kpartx/gpt.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "crc32.h" #include "kpartx.h" @@ -64,6 +65,12 @@ #define BLKGETSIZE64 _IOR(0x12,114,sizeof(uint64_t)) /* return device size in bytes (u64 *arg) */ #endif +/* + * The max size of a partition table that we will read for calculating the CRC. + * Note that this is much larger than MAX_SLICES. It's 0x02000000 on most systems. + */ +#define MAX_PT_SIZE ((SIZE_MAX <= UINT32_MAX ? SIZE_MAX : UINT32_MAX) / sizeof(gpt_entry)) + struct blkdev_ioctl_param { unsigned int block; size_t content_length; @@ -82,8 +89,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); } @@ -234,24 +240,48 @@ read_lba(int fd, uint64_t lba, void *buffer, size_t bytes) * Allocates space for PTEs based on information found in @gpt. * Notes: remember to free pte when you're done! */ -static gpt_entry * -alloc_read_gpt_entries(int fd, gpt_header * gpt) +static gpt_entry *alloc_read_gpt_entries(int fd, gpt_header *gpt, unsigned int ns) { - gpt_entry *pte; - size_t count = __le32_to_cpu(gpt->num_partition_entries) * - __le32_to_cpu(gpt->sizeof_partition_entry); + gpt_entry *pte = NULL; + size_t num_entries = __le32_to_cpu(gpt->num_partition_entries); + 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 (!count) return NULL; + 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 (aligned_malloc((void **)&pte, get_sector_size(fd), &count)) + if (pe_map == MAP_FAILED) { + fprintf(stderr, "%s: mmap failed: %s\n", __func__, strerror(errno)); return NULL; - memset(pte, 0, count); + } - if (!read_lba(fd, __le64_to_cpu(gpt->partition_entry_lba), pte, - count)) { - free(pte); - 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; } @@ -290,9 +320,8 @@ 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; @@ -346,28 +375,16 @@ is_gpt_valid(int fd, uint64_t lba, return 0; } - if (!(*ptes = alloc_read_gpt_entries(fd, *gpt))) { - free(*gpt); - *gpt = NULL; - return 0; - } - - /* 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"); + if (!(*ptes = alloc_read_gpt_entries(fd, *gpt, ns))) { free(*gpt); *gpt = NULL; - free(*ptes); - *ptes = NULL; return 0; } /* We're done, all's well */ return 1; } + /** * compare_gpts() - Search disk for valid GPT headers and PTEs * @pgpt is the primary GPT header @@ -489,8 +506,7 @@ compare_gpts(gpt_header *pgpt, gpt_header *agpt, uint64_t lastlba) * Validity depends on finding either the Primary GPT header and PTEs valid, * or the Alternate GPT header and PTEs valid, and the PMBR valid. */ -static int -find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes) +static int find_valid_gpt(int fd, gpt_header **gpt, gpt_entry **ptes, unsigned int ns) { int good_pgpt = 0, good_agpt = 0, good_pmbr = 0; gpt_header *pgpt = NULL, *agpt = NULL; @@ -503,20 +519,17 @@ find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes) if (!(lastlba = last_lba(fd))) return 0; - good_pgpt = is_gpt_valid(fd, GPT_PRIMARY_PARTITION_TABLE_LBA, - &pgpt, &pptes); + good_pgpt = is_gpt_valid(fd, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, + &pptes, ns); if (good_pgpt) { - good_agpt = is_gpt_valid(fd, - __le64_to_cpu(pgpt->alternate_lba), - &agpt, &aptes); + good_agpt = is_gpt_valid(fd, __le64_to_cpu(pgpt->alternate_lba), + &agpt, &aptes, ns); if (!good_agpt) { - good_agpt = is_gpt_valid(fd, lastlba, - &agpt, &aptes); + good_agpt = is_gpt_valid(fd, lastlba, &agpt, &aptes, ns); } } else { - good_agpt = is_gpt_valid(fd, lastlba, - &agpt, &aptes); + good_agpt = is_gpt_valid(fd, lastlba, &agpt, &aptes, ns); } /* The obviously unsuccessful case */ @@ -609,7 +622,7 @@ read_gpt_pt (int fd, __attribute__((unused)) struct slice all, int last_used_index = -1; int sector_size_mul = get_sector_size(fd)/512; - if (!find_valid_gpt (fd, &gpt, &ptes) || !gpt || !ptes) { + if (!find_valid_gpt(fd, &gpt, &ptes, ns) || !gpt || !ptes) { if (gpt) free (gpt); if (ptes) diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx index d3c9aef51..32a5aef01 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,33 +27,44 @@ # 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:=}" # 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; 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 INT TERM trap 'cleanup' 0 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 == +$CLEANUP +== END CLEANUP ==\"" fi eval "$CLEANUP" &>/dev/null } push_cleanup() { - CLEANUP="$@;$CLEANUP" + CLEANUP="$* +$CLEANUP" } pop_cleanup() { @@ -59,30 +73,68 @@ 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 + # 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() { - dd if=/dev/zero of=$1 bs=1b count=1 + dd if=/dev/zero of="$1" bs=1b count=1 &>/dev/null +} + +current_state() { + [[ "$KPARTX_DEBUG" ]] || return 0 + echo "--------------------" + 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 +} + +wait_a_moment() { + sleep "$(printf %d.%06d "$((WAIT_USEC / 1000000))" "$((WAIT_USEC % 1000000))")" } step preparation -[[ $UID -eq 0 ]] -[[ $KPARTX ]] || { - if [[ -x $PWD/kpartx/kpartx ]]; then +[[ "$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 KPARTX=$PWD/kpartx/kpartx else - KPARTX=$(which kpartx) + KPARTX=$(command -v kpartx) fi } -[[ $KPARTX ]] +[[ "$KPARTX" ]] +echo "== Using kpartx = $KPARTX ==" >&2 + +# Try to use system kpartx for cleanup +# Fall back to ours if not found +# shellcheck disable=SC2034 +CLEANUP_KPARTX=$(command -v kpartx) || CLEANUP_KPARTX=$KPARTX FILE1=kpartx1 FILE2=kpartx2 @@ -91,43 +143,63 @@ 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 -LVMCONF='devices { filter = [ "a|/dev/loop.*|", r".*" ] }' - +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"' } +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 + # 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 - -step "create DM devices (spans)" +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") + +wait_a_moment + +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. @@ -138,198 +210,254 @@ 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' +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 < $VG-$LV, $USER1" # 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' -usleep $WAIT_US - -[[ -b /dev/mapper/$VG-$LV ]] +# shellcheck disable=SC2090 +pvcreate ${LVMCONF:+--config "$LVMCONF"} -f "$LO3" +# shellcheck disable=SC2090 +vgcreate ${LVMCONF:+--config "$LVMCONF"} "$VG" "$LO3" +push_cleanup 'vgremove ${LVMCONF:+--config "$LVMCONF"} -f "$VG"' +# shellcheck disable=SC2090 +lvcreate ${LVMCONF:+--config "$LVMCONF"} -L "$((SIZE/2))B" -n "$LV" "$VG" +push_cleanup 'lvremove ${LVMCONF:+--config "$LVMCONF"} -f "$VG/$LV"' +wait_a_moment + +current_state +[[ -b "/dev/mapper/$VG-$LV" ]] # 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 +mk_partitions "$LO1" +mk_partitions "$LO2" +mk_partitions "$LO4" -# Test invocation of kpartx with regular file here -LO2P1=/dev/mapper/$(basename $LO2)-foo1 -$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 -$KPARTX $KPARTX_OPTS -d $LO2 -[[ ! -b $LO2P1 ]] - -mk_partitions $LO2 -$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)' - -usleep $WAIT_US -[[ -b $LO1P1 ]] -[[ -b $LO2P1 ]] +# 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 "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 on $LO2 => $LO2P1" +mk_partitions "$LO2" +# shellcheck disable=2086 +$KPARTX $KPARTX_OPTS -a -p -foo "$FILE2" +current_state +[[ -b "$LO2P1" ]] + +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" + +wait_a_moment +current_state +[[ -b "$LO1P1" ]] +[[ -b "$LO2P1" ]] # dmsetup info $LO2P1 +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) +dmsetup rename "$(basename "$LO1P1")" "$(basename "$LO1")" +LO1P1=/dev/mapper/$(basename "$LO1") pop_cleanup -push_cleanup 'dmsetup remove -f $(basename $LO1P1)' - -# dmsetup info $LO1P1 +push_cleanup 'dmsetup remove -f "$(basename "$LO1P1")"' -step "create partitions on DM devices" -mk_partitions /dev/mapper/$SPAN2 +current_state +[[ -b "$LO1P1" ]] -$KPARTX $KPARTX_OPTS -a -p -bar /dev/mapper/$SPAN2 +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" +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 # set the UUID). Remove them using -f -push_cleanup '$KPARTX $KPARTX_OPTS -f -d /dev/mapper/$SPAN2' -push_cleanup 'dmsetup remove -f $(basename $SPAN2P1)' +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 +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 # see above -push_cleanup '$KPARTX $KPARTX_OPTS -f -d /dev/mapper/$SPAN1' -push_cleanup 'dmsetup remove -f $(basename $SPAN1P1)' - -usleep $WAIT_US -[[ -b $SPAN2P1 ]] -[[ -b $SPAN1P1 ]] - -step "rename partitions on DM device to default" -$KPARTX $KPARTX_OPTS -u /dev/mapper/$SPAN1 -[[ ! -b ${SPAN1P1} ]] +push_cleanup '$CLEANUP_KPARTX $KPARTX_OPTS -f -d "/dev/mapper/$SPAN1"' +push_cleanup 'dmsetup remove -f "$(basename "$SPAN1P1")"' + +wait_a_moment +current_state +[[ -b "$SPAN2P1" ]] +[[ -b "$SPAN1P1" ]] + +step "rename partitions on $SPAN1 with default delimiter => $SPAN1P1 -> $(partname "$SPAN1" 1)" +# 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/} ]] - -step "rename partitions on DM device back from default" -$KPARTX $KPARTX_OPTS -u -p -spam /dev/mapper/$SPAN1 -[[ -b ${SPAN1P1} ]] -[[ ! -b ${SPANP1//-foo/} ]] - -step "delete partitions on DM devices" -$KPARTX $KPARTX_OPTS -d /dev/mapper/$SPAN1 >&2 -usleep $WAIT_US - -[[ -b $SPAN2P1 ]] -[[ -b $LO1P1 ]] -[[ -b $LO2P1 ]] -[[ ! -b $SPAN1P1 ]] - -$KPARTX $KPARTX_OPTS -d /dev/mapper/$SPAN2 -usleep $WAIT_US - -[[ -b $LO1P1 ]] -[[ -b $LO2P1 ]] -[[ ! -b $SPAN2P1 ]] - -step "rename partitions on loop device" -$KPARTX $KPARTX_OPTS -u -p -spam $LO2 -[[ ! -b ${LO2P1} ]] -[[ -b ${LO2P1//-foo/-spam} ]] - -step "rename partitions on loop device back" -$KPARTX $KPARTX_OPTS -u -p -foo $LO2 -[[ -b ${LO2P1} ]] -[[ ! -b ${LO2P1//-foo/-spam} ]] - -step "rename partitions on loop device to default" -$KPARTX $KPARTX_OPTS -u $LO2 -#read a -[[ ! -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 -[[ -b ${LO2P1} ]] -[[ ! -b ${LO2P1//-foo/p} ]] - -step "rename partitions on loop devices" -$KPARTX $KPARTX_OPTS -u -p spam $LO2 - -step "delete partitions on loop devices" - -$KPARTX $KPARTX_OPTS -d $LO3 - +[[ -b "$(partname "$SPAN1" 1)" ]] + +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 device $SPAN1 => -$SPAN1P1" +# shellcheck disable=2086 +$KPARTX $KPARTX_OPTS -d "/dev/mapper/$SPAN1" >&2 +wait_a_moment + +current_state +[[ -b "$SPAN2P1" ]] +[[ -b "$LO1P1" ]] +[[ -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 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 $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 $LO2 with default delimiter => $LO2P1 -> $(partname "$LO2" 1)" +# shellcheck disable=2086 +$KPARTX $KPARTX_OPTS -u "$LO2" +wait_a_moment +current_state +[[ ! -b "$LO2P1" ]] +[[ -b "$(partname "$LO2" 1)" ]] + +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 $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 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 -$KPARTX $KPARTX_OPTS -d $FILE2 -$KPARTX $KPARTX_OPTS -d $LO1 -usleep $WAIT_US +# 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 +current_state +[[ ! -b "$LO1P1" ]] + +pop_cleanup # 'dmsetup remove -f $(basename "$SPAN1P1")' +[[ ! -b "$LO2P1" ]] +pop_cleanup # '$CLEANUP_KPARTX $KPARTX_OPTS -f -d /dev/mapper/$SPAN1' -# ls -l /dev/mapper -[[ ! -b $LO1P1 ]] -pop_cleanup -[[ ! -b $LO2P1 ]] -pop_cleanup # spans should not have been removed -[[ -b /dev/mapper/$SPAN1 ]] -[[ -b /dev/mapper/$SPAN2 ]] -[[ -b /dev/mapper/$USER1 ]] +current_state +[[ -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 diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index abf84486e..9e0264c09 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -1193,6 +1193,18 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, if (out_len <= 1) return 0; + /* + * Not a valid SPC-2/3 vpd page 83. Assume it's a SCSI-2 style + * descriptor. + */ + if (in_len >= 20 && in[6] != 0) { + len = 0; + vpd_type = 0x3; + vpd_len = in_len - 4; + vpd = in + 4; + goto decode; + } + d = in + 4; while (d <= in + in_len - 4) { bool invalid = false; @@ -1308,6 +1320,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, vpd_type = vpd[1] & 0xf; vpd_len = vpd[3]; vpd += 4; +decode: /* untaint vpd_len for coverity */ if (vpd_len > WWID_SIZE) { condlog(1, "%s: suspicious designator length %zu truncated to %u", diff --git a/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/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) { diff --git a/libmultipath/prioritizers/datacore.c b/libmultipath/prioritizers/datacore.c index f288306c2..99bbd69a8 100644 --- a/libmultipath/prioritizers/datacore.c +++ b/libmultipath/prioritizers/datacore.c @@ -51,13 +51,12 @@ int datacore_prio (const char *dev, int sg_fd, char * args, return 0; } - if (sscanf(args, "timeout=%i preferredsds=%s", - (int *)&timeout_ms, preferredsds) == 2) {} - else if (sscanf(args, "preferredsds=%s timeout=%i", - preferredsds, (int *)&timeout_ms) == 2) {} - else if (sscanf(args, "preferredsds=%s", - 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 f5fdd42aa..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=%s", 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); } diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 664e62fca..1cd9187a5 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); @@ -640,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/libmultipath/version.h b/libmultipath/version.h index 03d838fc1..32e028951 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -20,9 +20,9 @@ #ifndef VERSION_H_INCLUDED #define VERSION_H_INCLUDED -#define VERSION_CODE 0x000B04 +#define VERSION_CODE 0x000B05 /* MMDDYY, in hex */ -#define DATE_CODE 0x01141A +#define DATE_CODE 0x070D1A #define PROG "multipath-tools" diff --git a/multipathd/main.c b/multipathd/main.c index c8f004c95..9156b57f7 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -2441,8 +2441,10 @@ get_new_state(struct path *pp) * Wait for uevent for removed paths; * some LLDDs like zfcp keep paths unavailable * without sending uevents. + * Also, map PATH_TIMEOUT to PATH_DOWN here, like we do in + * pathinfo(). */ - if (newstate == PATH_REMOVED) + if (newstate == PATH_REMOVED || newstate == PATH_TIMEOUT) newstate = PATH_DOWN; if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) { diff --git a/tests/vpd.c b/tests/vpd.c index a72865337..9d0560c91 100644 --- a/tests/vpd.c +++ b/tests/vpd.c @@ -331,6 +331,27 @@ 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 +500,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) @@ -779,40 +824,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); static int test_vpd(void) { @@ -945,6 +997,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); }