mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
configured. Both of the files pick the same free loop device in the for loop, and then one fails to losetup. This patch will now retry if losetup fails until it succeeds or no more devices are available. Signed-off-by: Robert Read <robert@xensource.com>
283 lines
5.2 KiB
Bash
283 lines
5.2 KiB
Bash
#!/bin/sh
|
|
|
|
set -x
|
|
|
|
dir=$(dirname "$0")
|
|
. "$dir/block-common.sh"
|
|
|
|
expand_dev() {
|
|
local dev
|
|
case $1 in
|
|
/*)
|
|
dev=$1
|
|
;;
|
|
*)
|
|
dev=/dev/$1
|
|
;;
|
|
esac
|
|
echo -n $dev
|
|
}
|
|
|
|
|
|
canonicalise_mode()
|
|
{
|
|
local mode="$1"
|
|
|
|
if ! expr index "$mode" 'w' >/dev/null
|
|
then
|
|
echo 'ro'
|
|
elif ! expr index "$mode" '!' >/dev/null
|
|
then
|
|
echo 'rw'
|
|
else
|
|
echo 'no'
|
|
fi
|
|
}
|
|
|
|
|
|
##
|
|
# check_sharing device device_major_minor mode
|
|
#
|
|
# Check whether the device requested is already in use. To use the device in
|
|
# read-only mode, it may be in use in read-only mode, but may not be in use in
|
|
# read-write anywhere at all. To use the device in read-write mode, it must
|
|
# not be in use anywhere at all.
|
|
#
|
|
check_sharing()
|
|
{
|
|
local dev="$1"
|
|
local mode="$2"
|
|
|
|
local devmm=$(device_major_minor "$dev")
|
|
local file
|
|
|
|
if [ "$mode" == 'rw' ]
|
|
then
|
|
toskip="^$"
|
|
else
|
|
toskip="^[^ ]* [^ ]* [^ ]* ro "
|
|
fi
|
|
|
|
for file in $(cat /proc/mounts | grep -v "$toskip" | cut -f 1 -d ' ')
|
|
do
|
|
if [ -e "$file" ]
|
|
then
|
|
local d=$(device_major_minor "$file")
|
|
|
|
if [ "$d" == "$devmm" ]
|
|
then
|
|
echo 'local'
|
|
return
|
|
fi
|
|
fi
|
|
done
|
|
|
|
for file in /sys/devices/xen-backend/*/physical_device
|
|
do
|
|
if [ -e "$file" ] # Cope with no devices, i.e. the * above did not expand.
|
|
then
|
|
local d=$(cat "$file")
|
|
if [ "$d" == "$devmm" ]
|
|
then
|
|
if [ "$mode" == 'rw' ]
|
|
then
|
|
echo 'guest'
|
|
return
|
|
else
|
|
local m=$(cat "${file/physical_device/mode}")
|
|
|
|
if expr index "$m" 'w' >/dev/null
|
|
then
|
|
echo 'guest'
|
|
return
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
|
|
echo 'ok'
|
|
}
|
|
|
|
|
|
check_device_sharing()
|
|
{
|
|
local dev="$1"
|
|
local mode=$(canonicalise_mode "$2")
|
|
local result
|
|
|
|
if [ "$mode" == 'no' ]
|
|
then
|
|
return 0
|
|
fi
|
|
|
|
result=$(check_sharing "$dev" "$mode")
|
|
|
|
if [ "$result" != 'ok' ]
|
|
then
|
|
do_ebusy "Device $dev is mounted " "$mode" "$result"
|
|
fi
|
|
}
|
|
|
|
|
|
check_file_sharing()
|
|
{
|
|
local file="$1"
|
|
local dev="$2"
|
|
local mode="$3"
|
|
|
|
result=$(check_sharing "$dev" "$mode")
|
|
|
|
if [ "$result" != 'ok' ]
|
|
then
|
|
do_ebusy "File $file is loopback-mounted through $dev,
|
|
which is mounted " "$mode" "$result"
|
|
fi
|
|
}
|
|
|
|
|
|
do_ebusy()
|
|
{
|
|
local prefix="$1"
|
|
local mode="$2"
|
|
local result="$3"
|
|
|
|
if [ "$result" == 'guest' ]
|
|
then
|
|
dom='a guest '
|
|
when='now'
|
|
else
|
|
dom='the privileged '
|
|
when='by a guest'
|
|
fi
|
|
|
|
if [ "$mode" == 'rw' ]
|
|
then
|
|
m1=''
|
|
m2=''
|
|
else
|
|
m1='read-write '
|
|
m2='read-only '
|
|
fi
|
|
|
|
ebusy \
|
|
"${prefix}${m1}in ${dom}domain,
|
|
and so cannot be mounted ${m2}${when}."
|
|
}
|
|
|
|
|
|
t=$(xenstore_read_default "$XENBUS_PATH/type" 'MISSING')
|
|
|
|
case "$command" in
|
|
add)
|
|
phys=$(xenstore_read_default "$XENBUS_PATH/physical-device" 'MISSING')
|
|
if [ "$phys" != 'MISSING' ]
|
|
then
|
|
# Depending upon the hotplug configuration, it is possible for this
|
|
# script to be called twice, so just bail.
|
|
exit 0
|
|
fi
|
|
|
|
p=$(xenstore_read "$XENBUS_PATH/params")
|
|
mode=$(xenstore_read "$XENBUS_PATH/mode")
|
|
|
|
case $t in
|
|
phy)
|
|
dev=$(expand_dev $p)
|
|
check_device_sharing "$dev" "$mode"
|
|
write_dev "$dev"
|
|
exit 0
|
|
;;
|
|
|
|
file)
|
|
# Canonicalise the file, for sharing check comparison, and the mode
|
|
# for ease of use here.
|
|
file=$(readlink -f "$p")
|
|
mode=$(canonicalise_mode "$mode")
|
|
|
|
if [ "$mode" == 'rw' ] && ! stat "$file" -c %A | grep w >&/dev/null
|
|
then
|
|
ebusy \
|
|
"File $file is read-only, and so I will not
|
|
mount it read-write in a guest domain."
|
|
fi
|
|
|
|
|
|
while true
|
|
do
|
|
loopdev=''
|
|
for dev in /dev/loop*
|
|
do
|
|
if [ ! -b "$dev" ]
|
|
then
|
|
continue
|
|
fi
|
|
|
|
f=$(losetup "$dev" 2>/dev/null) || f='()'
|
|
f=$(echo "$f" | sed -e 's/.*(\(.*\)).*/\1/g')
|
|
|
|
log err "$file $f $dev"
|
|
|
|
if [ "$f" ]
|
|
then
|
|
# $dev is in use. Check sharing.
|
|
if [ "$mode" == 'no' ]
|
|
then
|
|
continue
|
|
fi
|
|
|
|
f=$(readlink -f "$f")
|
|
|
|
if [ "$f" == "$file" ]
|
|
then
|
|
check_file_sharing "$file" "$dev" "$mode"
|
|
fi
|
|
else
|
|
# $dev is not in use, so we'll remember it for use later; we want
|
|
# to finish the sharing check first.
|
|
|
|
if [ "$loopdev" == '' ]
|
|
then
|
|
loopdev="$dev"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
if [ "$loopdev" == '' ]
|
|
then
|
|
fatal 'Failed to find an unused loop device'
|
|
fi
|
|
if losetup "$loopdev" "$file"
|
|
then
|
|
log err "mapped $file using $loopdev"
|
|
xenstore_write "$XENBUS_PATH/node" "$loopdev"
|
|
write_dev "$loopdev"
|
|
exit 0
|
|
else
|
|
log err "losetup $loopdev $file failed, retry"
|
|
fi
|
|
done
|
|
;;
|
|
esac
|
|
;;
|
|
|
|
remove)
|
|
case $t in
|
|
phy)
|
|
exit 0
|
|
;;
|
|
|
|
file)
|
|
node=$(xenstore_read "$XENBUS_PATH/node")
|
|
losetup -d "$node"
|
|
exit 0
|
|
;;
|
|
esac
|
|
;;
|
|
|
|
esac
|
|
|
|
# If we've reached here, $t is neither phy nor file, so fire a helper script.
|
|
[ -x /etc/xen/scripts/block-"$t" ] && \
|
|
/etc/xen/scripts/block-"$t" "$command" $node
|