Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions common/rc
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,22 @@ _systemd_start_udevd() {
return 0
}

_have_systemd_ver() {
local required_ver=${1}
local ver

if ! command -v systemctl &>/dev/null; then
SKIP_REASONS+=("systemd is not available")
return 1
fi

ver=$(systemctl --version | head -1 | sed 's/systemd //;s/[^0-9].*//')
if ((ver < required_ver)); then
SKIP_REASONS+=("systemd version is older than ${required_ver}")
return 1
fi
}

# Run the given command as NORMAL_USER
_run_user() {
su "$NORMAL_USER" -c "$1"
Expand Down
77 changes: 77 additions & 0 deletions tests/loop/012
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-3.0+
# Copyright (C) 2026 Daan De Meyer
#
# Regression test for a race between udev and loop_reread_partitions().
#
# When LOOP_CONFIGURE is called with LO_FLAGS_PARTSCAN,
# disk_force_media_change() used to set GD_NEED_PART_SCAN before the
# uevent was sent. When udev opened the device in response,
# blkdev_get_whole() would trigger a partition scan, and then
# loop_reread_partitions() would scan again. The second scan drops all
# partitions from the first scan before re-adding them, causing
# partition devices to briefly disappear.
#
# Verify that setting up a loop device with partscan does not produce
# spurious partition add/remove events.

. tests/loop/rc

DESCRIPTION="check for spurious partition removal when partscan is enabled"
TIMED=1

requires() {
_have_program sfdisk
_have_systemd_ver 259
}

test() {
echo "Running ${TEST_NAME}"

truncate -s 3MiB "$TMPDIR/img"
sfdisk "$TMPDIR/img" >"$FULL" 2>&1 <<-EOF
label: gpt
size=1MiB
EOF

local dev
dev="$(losetup -f)"

# Monitor kernel uevents for partition block devices.
udevadm monitor --kernel -s block/partition >"$TMPDIR/uevents" 2>&1 &
local mon_pid=$!
# Give the monitor time to set up its netlink socket.
sleep 0.5

local iterations=0
SECONDS=0
while ((SECONDS < "${TIMEOUT:-5}")); do
if ! losetup -P "$dev" "$TMPDIR/img" 2>>"$FULL"; then
continue
fi
losetup -d "$dev" 2>>"$FULL"
((iterations++))
done

sleep 0.5
kill "$mon_pid"
wait "$mon_pid" 2>/dev/null

# Each setup+teardown cycle should produce exactly one add and one
# remove kernel uevent for the partition device. If the race
# triggers, a second partition scan produces an extra remove+add
# pair, inflating the counts beyond the number of iterations.
local name="${dev##*/}"
local adds removes
adds=$(grep -c "^KERNEL\[.*\] add.*${name}p" "$TMPDIR/uevents")
removes=$(grep -c "^KERNEL\[.*\] remove.*${name}p" "$TMPDIR/uevents")

if ((adds > iterations)); then
echo "Fail: $iterations iterations but $adds add events (expected $iterations)"
fi
if ((removes > iterations)); then
echo "Fail: $iterations iterations but $removes remove events (expected $iterations)"
fi

echo "Test complete"
}
2 changes: 2 additions & 0 deletions tests/loop/012.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Running loop/012
Test complete