#!/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 is_attached /sys$DEVPATH; then return 0 fi if qubesdb-read -q "$QDB_KEY/desc" >/dev/null; then qubesdb-rm "$QDB_KEY/" qubesdb-write /qubes-block-devices '' fi } is_used() { local sys_devpath=$1 local devname=$(grep ^DEVNAME= $sys_devpath/uevent | cut -f 2 -d =) # mounted; or enabled swap if lsblk -dnr -o MOUNTPOINT "/dev/$devname" | grep -q .; 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 } refresh_another() { # launch this script for other device local devpath=$1 local launch_env=$(udevadm info -q all -x -p "$devpath" \ | grep ^E: | cut -d ' ' -f 2- | tr ' ' ':') env -i PATH=$PATH $launch_env $0 } # 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)) if [ -z "$dev_hex" -o "$dev_hex" = "0:0" ]; then return 1 fi $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)) refresh_another /$(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 refresh_another /$(realpath --relative-to=/sys $dev) done # cache slave devices for remove event if [ -n "$DM_NAME" ]; then ls -1 /sys$DEVPATH/slaves/ > /var/run/qubes/block-slave-cache-$NAME fi # then take care of this device: # udev rules already excluded this device: if [ "$DM_UDEV_DISABLE_DISK_RULES_FLAG" = "1" ]; then xs_remove exit 0 fi # 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 || 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 loop devices from excluded directories if [[ "$NAME" = 'loop'* ]]; then backing_file=$(cat /sys/block/${NAME}/loop/backing_file) if [ -n "$backing_file" ]; then dir_to_check=$(dirname "$backing_file") while [ "$dir_to_check" != "/" -a "$dir_to_check" != "." ]; do if [ -e "$dir_to_check/.qubes-exclude-block-devices" ]; then xs_remove exit 0 fi dir_to_check=$(dirname "$dir_to_check") done fi 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 '' # Make sure that block backend is loaded /sbin/modprobe xen-blkback 2> /dev/null || /sbin/modprobe blkbk