qubes-linux-utils/udev/udev-block-add-change
Marek Marczykowski-Górecki a465359a5d
udev: fix deadlock on xenstore access during dom0 boot
During early dom0 boot, before xenstored is started any access to it
through /proc/xen/xenbus (or /dev/xen/xenbus) will wait until xenstored
is started. If that happens in script ordered to start before xenstored,
we have a deadlock. Prevent this by using `xenstore-ls -s` in dom0,
which will fail immediately when xenstored isn't running yet. This fail
isn't a problem, because when xenstored isn't running yet, surely device
isn't attached anywhere.

QubesOS/qubes-issues#1081

(cherry picked from commit cdbcb2eb55)
2016-06-21 04:32:30 +02:00

144 lines
3.5 KiB
Bash
Executable File

#!/bin/bash
shopt -s nullglob
export LC_CTYPE=en_US.UTF-8
NAME=${DEVNAME#/dev/}
DESC="`echo "${ID_MODEL} (${ID_FS_LABEL})" | iconv -f utf8 -t ascii//TRANSLIT`"
SIZE=$[ $(cat /sys/$DEVPATH/size) * 512 ]
MODE=w
QDB_KEY="/qubes-block-devices/$NAME"
xs_remove() {
if [ "$QUBES_EXPOSED" == "1" ]; then
qubesdb-rm "$QDB_KEY/"
qubesdb-write /qubes-block-devices ''
fi
echo QUBES_EXPOSED=0
}
is_used() {
local sys_devpath=$1
# mounted
if fgrep -q $(basename $sys_devpath) /proc/mounts; then
return 0
fi
# part of other device-mapper
if [ -n "`ls -A $sys_devpath/holders 2> /dev/null`" ]; then
return 0
fi
# open device-mapper device
if [ -f "$sys_devpath/dm/name" ] && \
/sbin/dmsetup info "$(cat $sys_devpath/dm/name)" |\
grep -q "^Open count:.*[1-9]"; then
return 0
fi
return 1
}
# communicate with xenstored through socket in dom0
# trying to access xenstore before xenstored is started, hang forever (in
# non-killable state), so better fail ('-s' in VM if /proc/xen isn't mounted
# yet) than hang dom0 boot
if [ ! -r /proc/xen/capabilities ] || grep -q control_d /proc/xen/capabilities; then
XENSTORE_LS="xenstore-ls -s"
else
XENSTORE_LS="xenstore-ls"
fi
is_attached() {
dev_hex=$(stat -c %t:%T /dev/$(basename $1))
$XENSTORE_LS backend/vbd | grep -q "physical-device = \"$dev_hex\""
}
# update info about parent devices, if any:
if [ -f /sys$DEVPATH/partition ]; then
parent=$(dirname $(readlink -f /sys$DEVPATH))
udevadm trigger \
--property-match=DEVPATH=/$(realpath --relative-to=/sys $parent)
# if parent device is already attached, skip its partitions
if is_attached $parent; then
xs_remove
exit 0
fi
fi
# and underlying devices of device-mapper (if any)
for dev in /sys$DEVPATH/slaves/*; do
udevadm trigger \
--property-match=DEVPATH=/$(realpath --relative-to=/sys $dev)
done
# then take care of this device:
# device itself is already used
if is_used /sys$DEVPATH; then
xs_remove
exit 0
fi
# or one of its partitions is used
# or already attached (prevent attaching both device and its partition(s) at
# the same time)
for part in /sys$DEVPATH/$NAME*; do
if [ -d $part ]; then
if is_used $part; then
xs_remove
exit 0
fi
if is_attached $part; then
xs_remove
exit 0
fi
fi
done
# or "empty" loop device
if [ "$MAJOR" -eq 7 -a ! -d /sys/$DEVPATH/loop ]; then
xs_remove
exit 0
fi
# ... and temporary devices used during VM startup
if [[ "$NAME" = 'loop'* ]] && \
[[ "`cat /sys/block/${NAME%p*}/loop/backing_file`" = \
'/var/lib/qubes/'*'/volatile.img' ]]; then
xs_remove
exit 0
fi
# Check if device is read-only
if [ "`cat /sys/$DEVPATH/ro`" -eq 1 ]; then
MODE=r
fi
# Special case for CD
if [ "$ID_TYPE" = "cd" ]; then
if [ "$ID_CDROM_MEDIA" != "1" ]; then
# Hide empty cdrom drive
xs_remove
exit 0
fi
MODE=r
fi
# Special description for loop devices
if [ -d /sys/$DEVPATH/loop ]; then
DESC=$(cat /sys/$DEVPATH/loop/backing_file)
fi
# and for device-mapper
if [ -n "$DM_NAME" ]; then
DESC="$DM_NAME"
fi
# The last one is meant to trigger watches
qubesdb-write \
"$QDB_KEY/desc" "$DESC" \
"$QDB_KEY/size" "$SIZE" \
"$QDB_KEY/mode" "$MODE" \
/qubes-block-devices ''
echo QUBES_EXPOSED=1
# Make sure that block backend is loaded
/sbin/modprobe xen-blkback 2> /dev/null || /sbin/modprobe blkbk