openzfs / zfs

OpenZFS on Linux and FreeBSD

Home Page:https://openzfs.github.io/openzfs-docs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Incorrect test for file existence in vdev_id

OsmiumBalloon opened this issue · comments

Summary

The /lib/udev/vdev_id script attempts to test for the existence of a file using test(1) option -e and a shell glob * (star); this fails if multiple files exist.

Environment

  • Debian 12.5 "bookworm"
  • Kernel 6.1.0-21-amd64 / 6.1.90-1 (2024-05-03)
  • zfsutils 2.1.11-1
  • bash 5.2.15-2+b2
  • dash 0.5.12-2

Symptoms

In the process of debugging unrelated issues, I set udev_log to debug in /etc/udev/udev.conf. I then began seeing errors like this in syslog:

Jun 12 10:37:18 sat-fs484 (udev-worker)[3231]: dm-3: '/lib/udev/vdev_id -d dm-3'(err) '/lib/udev/vdev_id: 307: [: /sys/block/dm-3/slaves/sdcj: unexpected operator'

Investigation

In file/lib/udev/vdev_id, in function sas_handler(), on line 307, the following shell code appears:

if [ ! -e /sys/block/$DMDEV/slaves/* ]  ; then

Here, $DMDEV will normally be a bare device mapper device name, like dm-3. So, taking that example and substituting the variable, the resulting code would be:

if [ ! -e /sys/block/dm-3/slaves/* ]  ; then

The sysfs directory in question lists the underlying devices for a device mapper virtual device. For the normal case in a multipath configuration, the shell glob will match two or more devices, yielding a command line that looks like this:

if [ ! -e /sys/block/dm-0/slaves/sdbp /sys/block/dm-0/slaves/sde ]; then

That is a syntax error; the -e switch to test(1) takes one and only one argument. This can be demonstrated at the command line with both GNU Bash:

$ [ ! -e /sys/block/dm-0/slaves/sdbp /sys/block/dm-0/slaves/sde ]
bash: [: /sys/block/dm-0/slaves/sdbp: binary operator expected

as well as the default Debian script shell, Dash:

$ [ ! -e /sys/block/dm-0/slaves/sdbp /sys/block/dm-0/slaves/sde ]
dash: 1: [: /sys/block/dm-0/slaves/sdbp: unexpected operator

Resolution

The intent of the code appears to be to test if any slave devices exist, and fallback to an alternate if none exist. I believe the following will accomplish this in a reasonably robust fashion:

if [ -n "$( find /sys/block/$DMDEV/slaves/ -maxdepth 0 -empty )" ]  ; then

This asks find(1) to report empty files or directories, at a maximum depth of zero -- so only the given path itself is tested. If the resulting output is non-empty (-n switch), then the path passed the test (was empty), so there are no slaves.

This does assume that the local find(1) implementation supports the -maxdepth option and -empty test, both of which are non-POSIX extensions, but which are present in both the GNU and FreeBSD implementations.