1#!/bin/bash 2 3dir=$(dirname "$0") 4. "$dir/block-common.sh" 5 6expand_dev() { 7 local dev 8 case $1 in 9 /*) 10 dev=$1 11 ;; 12 *) 13 dev=/dev/$1 14 ;; 15 esac 16 echo -n $dev 17} 18 19find_free_loopback_helper() { 20 local next_devnum=0 21 local busy_devnum 22 while read busy_devnum; do 23 if [ "$next_devnum" != "$busy_devnum" ]; then 24 break 25 fi 26 let next_devnum=$next_devnum+1 27 done 28 echo "/dev/loop${next_devnum}" 29} 30 31# Not all distros have "losetup -f" 32find_free_loopback_dev() { 33 local loopdev 34 loopdev=$(losetup -a | sed -e 's+^/dev/loop++' -e 's/:.*//' | find_free_loopback_helper) 35 if [ -n "$loopdev" ] && [ -b "$loopdev" ]; then 36 echo "$loopdev" 37 fi 38} 39 40## 41# check_sharing devtype device mode [inode] 42# 43# Check whether the device requested is already in use. To use the device in 44# read-only mode, it may be in use in read-only mode, but may not be in use in 45# read-write anywhere at all. To use the device in read-write mode, it must 46# not be in use anywhere at all. 47# 48# Prints one of 49# 50# 'local $d': the device ($d) may not be used because it is mounted in the 51# current (i.e. the privileged domain) in a way incompatible 52# with the requested mode; 53# 'guest $d': the device may not be used because it is already mounted 54# through device $d by a guest in a way incompatible with the 55# requested mode; or 56# 'ok': the device may be used. 57# 58check_sharing() 59{ 60 local devtype=$1 61 local dev="$2" 62 local mode="$3" 63 local devmm="," 64 65 if [ "$devtype" = "file" ]; 66 then 67 local inode="$4" 68 69 shared_list=$(losetup -a | 70 sed -n -e "s@^\([^:]\+\)\(:[[:blank:]]\[0*${dev}\]:${inode}[[:blank:]](.*)\)@\1@p" ) 71 for dev in $shared_list 72 do 73 if [ -n "$dev" ] 74 then 75 devmm="${devmm}$(device_major_minor $dev)," 76 fi 77 done 78 # if $devmm is unchanged, file being checked is not a shared loopback device 79 if [ "$devmm" = "," ]; 80 then 81 echo 'ok' 82 return 83 fi 84 else 85 devmm=${devmm}$(device_major_minor "$dev")"," 86 fi 87 88 local file 89 90 if [ "$mode" = 'w' ] 91 then 92 toskip="^$" 93 else 94 toskip="^[^ ]* [^ ]* [^ ]* ro[, ]" 95 fi 96 97 for file in $(cat /proc/mounts | grep -v "$toskip" | cut -f 1 -d ' ') 98 do 99 if [ -e "$file" ] 100 then 101 local d=$(device_major_minor "$file") 102 103 # checking for $d in $devmm is best through the [[...]] bashism 104 if [[ "$devmm" == *",$d,"* ]] 105 then 106 echo "local $d" 107 return 108 fi 109 fi 110 done 111 112 local base_path="$XENBUS_BASE_PATH/$XENBUS_TYPE" 113 for dom in $(xenstore-list "$base_path") 114 do 115 for dev in $(xenstore-list "$base_path/$dom") 116 do 117 d=$(xenstore_read_default "$base_path/$dom/$dev/physical-device" "") 118 119 # checking for $d in $devmm is best through the [[...]] bashism 120 if [ -n "$d" ] && [[ "$devmm" == *",$d,"* ]] 121 then 122 if [ "$mode" = 'w' ] 123 then 124 if ! same_vm $dom 125 then 126 echo "guest $d" 127 return 128 fi 129 else 130 local m=$(xenstore_read_default "$base_path/$dom/$dev/mode" "") 131 m=$(canonicalise_mode "$m") 132 133 if [ "$m" = 'w' ] 134 then 135 if ! same_vm $dom 136 then 137 echo "guest $d" 138 return 139 fi 140 fi 141 fi 142 fi 143 done 144 done 145 146 echo 'ok' 147} 148 149 150## 151# check_device_sharing dev mode 152# 153# Perform the sharing check for the given physical device and mode. 154# 155check_device_sharing() 156{ 157 local dev="$1" 158 local mode=$(canonicalise_mode "$2") 159 local type="device" 160 local result 161 162 if [ "x$mode" = 'x!' ] 163 then 164 return 0 165 fi 166 167 result=$(check_sharing "$type" "$dev" "$mode") 168 169 if [ "$result" != 'ok' ] 170 then 171 do_ebusy "Device $dev is mounted " "$mode" "${result%% *}" 172 fi 173} 174 175 176## 177# check_device_sharing file dev mode inode 178# 179# Perform the sharing check for the given file, with its corresponding 180# device, inode and mode. As the file can be mounted multiple times, 181# the inode is passed through to check_sharing for all instances to be 182# checked. 183# 184check_file_sharing() 185{ 186 local file="$1" 187 local dev="$2" 188 local mode="$3" 189 local inode="$4" 190 local type="file" 191 local result 192 193 result=$(check_sharing "$type" "$dev" "$mode" "$inode") 194 195 if [ "$result" != 'ok' ] 196 then 197 do_ebusy "File $file is loopback-mounted through ${result#* }, 198which is mounted " "$mode" "${result%% *}" 199 fi 200} 201 202 203## 204# do_ebusy prefix mode result 205# 206# Helper function for check_device_sharing check_file_sharing, calling ebusy 207# with an error message constructed from the given prefix, mode, and result 208# from a call to check_sharing. 209# 210do_ebusy() 211{ 212 local prefix="$1" 213 local mode="$2" 214 local result="$3" 215 216 if [ "$result" = 'guest' ] 217 then 218 dom='a guest ' 219 when='now' 220 else 221 dom='the privileged ' 222 when='by a guest' 223 fi 224 225 if [ "$mode" = 'w' ] 226 then 227 m1='' 228 m2='' 229 else 230 m1='read-write ' 231 m2='read-only ' 232 fi 233 234 release_lock "block" 235 ebusy \ 236"${prefix}${m1}in ${dom}domain, 237and so cannot be mounted ${m2}${when}." 238} 239 240 241t=$(xenstore_read_default "$XENBUS_PATH/type" 'MISSING') 242p=$(xenstore_read "$XENBUS_PATH/params") 243mode=$(xenstore_read "$XENBUS_PATH/mode") 244if [ -b "$p" ]; then 245 truetype="phy" 246elif [ -f "$p" ]; then 247 truetype="file" 248fi 249 250case "$command" in 251 add) 252 phys=$(xenstore_read_default "$XENBUS_PATH/physical-device" 'MISSING') 253 if [ "$phys" != 'MISSING' ] 254 then 255 # Depending upon the hotplug configuration, it is possible for this 256 # script to be called twice, so just bail. 257 exit 0 258 fi 259 260 FRONTEND_ID=$(xenstore_read "$XENBUS_PATH/frontend-id") 261 FRONTEND_UUID=$(xenstore_read_default \ 262 "/local/domain/$FRONTEND_ID/vm" 'unknown') 263 264 case $truetype in 265 phy) 266 dev=$(expand_dev $p) 267 268 if [ -L "$dev" ] 269 then 270 dev=$(readlink -f "$dev") || fatal "$dev link does not exist." 271 fi 272 test -e "$dev" || fatal "$dev does not exist." 273 test -b "$dev" || fatal "$dev is not a block device." 274 275 claim_lock "block" 276 check_device_sharing "$dev" "$mode" 277 write_dev "$dev" 278 release_lock "block" 279 exit 0 280 ;; 281 282 file) 283 # Canonicalise the file, for sharing check comparison, and the mode 284 # for ease of use here. 285 file=$(readlink -f "$p") || fatal "$p does not exist." 286 test -f "$file" || fatal "$file does not exist." 287 mode=$(canonicalise_mode "$mode") 288 289 claim_lock "block" 290 291 # Avoid a race with the remove if the path has been deleted, or 292 # otherwise changed from "InitWait" state e.g. due to a timeout 293 xenbus_state=$(xenstore_read_default "$XENBUS_PATH/state" 'unknown') 294 if [ "$xenbus_state" != '2' ] 295 then 296 release_lock "block" 297 fatal "Path closed or removed during hotplug add: $XENBUS_PATH state: $xenbus_state" 298 fi 299 300 if [ "$mode" = 'w' ] && ! stat "$file" -c %A | grep -q w 301 then 302 release_lock "block" 303 ebusy \ 304"File $file is read-only, and so I will not 305mount it read-write in a guest domain." 306 fi 307 308 if [ "x$mode" != 'x!' ] 309 then 310 inode=$(stat -c '%i' "$file") 311 dev=$(stat -c '%D' "$file") 312 if [ -z "$inode" ] || [ -z "$dev" ] 313 then 314 fatal "Unable to lookup $file: dev: $dev inode: $inode" 315 fi 316 317 check_file_sharing "$file" "$dev" "$mode" "$inode" 318 fi 319 320 loopdev=$(losetup -f 2>/dev/null || find_free_loopback_dev) 321 if [ "$loopdev" = '' ] 322 then 323 release_lock "block" 324 fatal 'Failed to find an unused loop device' 325 fi 326 327 if LANG=C losetup -h 2>&1 | grep read-only >/dev/null 328 then 329 roflag="-$mode"; roflag="${roflag#-w}"; roflag="${roflag#-!}" 330 else 331 roflag='' 332 fi 333 do_or_die losetup $roflag "$loopdev" "$file" 334 xenstore_write "$XENBUS_PATH/node" "$loopdev" 335 write_dev "$loopdev" 336 release_lock "block" 337 exit 0 338 ;; 339 340 "") 341 claim_lock "block" 342 success 343 release_lock "block" 344 ;; 345 esac 346 ;; 347 348 remove) 349 case $truetype in 350 phy) 351 exit 0 352 ;; 353 354 file) 355 claim_lock "block" 356 node=$(xenstore_read "$XENBUS_PATH/node") 357 losetup -d "$node" 358 release_lock "block" 359 exit 0 360 ;; 361 362 "") 363 exit 0 364 ;; 365 esac 366 ;; 367 368esac 369 370# If we've reached here, $t is neither phy nor file, so fire a helper script. 371[ -x ${XEN_SCRIPT_DIR}/block-"$t" ] && \ 372 ${XEN_SCRIPT_DIR}/block-"$t" "$command" 373