ia64/xen-unstable

view tools/examples/block @ 9536:d76ef15c9c95

The attached trivial patch fixes cases where a block device is mounted
read-only in domain 0, but still fails to be shared with a domU, because
it is mis-identified as being mounted writable. (On SUSE, CDs and DVDs
are auto-mounted not with merely permissions "ro", but
"ro,nosuid,nodev", hence the mis-identification.)

Signed-off-by: Charles Coffing <ccoffing@novell.com>
author kaf24@firebug.cl.cam.ac.uk
date Thu Mar 30 16:53:37 2006 +0100 (2006-03-30)
parents 871f768aadc6
children 4c2fab8f8c34
line source
1 #!/bin/sh
3 dir=$(dirname "$0")
4 . "$dir/block-common.sh"
6 expand_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 }
20 ##
21 # canonicalise_mode mode
22 #
23 # Takes the given mode, which may be r, w, ro, rw, w!, or rw!, or variations
24 # thereof, and canonicalises them to one of
25 #
26 # 'r': perform checks for a new read-only mount;
27 # 'w': perform checks for a read-write mount; or
28 # '!': perform no checks at all.
29 #
30 canonicalise_mode()
31 {
32 local mode="$1"
34 if ! expr index "$mode" 'w' >/dev/null
35 then
36 echo 'r'
37 elif ! expr index "$mode" '!' >/dev/null
38 then
39 echo 'w'
40 else
41 echo '!'
42 fi
43 }
46 ##
47 # check_sharing device mode
48 #
49 # Check whether the device requested is already in use. To use the device in
50 # read-only mode, it may be in use in read-only mode, but may not be in use in
51 # read-write anywhere at all. To use the device in read-write mode, it must
52 # not be in use anywhere at all.
53 #
54 # Prints one of
55 #
56 # 'local': the device may not be used because it is mounted in the current
57 # (i.e. the privileged domain) in a way incompatible with the
58 # requested mode;
59 # 'guest': the device may not be used because it already mounted by a guest
60 # in a way incompatible with the requested mode; or
61 # 'ok': the device may be used.
62 #
63 check_sharing()
64 {
65 local dev="$1"
66 local mode="$2"
68 local devmm=$(device_major_minor "$dev")
69 local file
71 if [ "$mode" == 'w' ]
72 then
73 toskip="^$"
74 else
75 toskip="^[^ ]* [^ ]* [^ ]* ro[, ]"
76 fi
78 for file in $(cat /proc/mounts | grep -v "$toskip" | cut -f 1 -d ' ')
79 do
80 if [ -e "$file" ]
81 then
82 local d=$(device_major_minor "$file")
84 if [ "$d" == "$devmm" ]
85 then
86 echo 'local'
87 return
88 fi
89 fi
90 done
92 local base_path="$XENBUS_BASE_PATH/$XENBUS_TYPE"
93 for dom in $(xenstore-list "$base_path")
94 do
95 for dev in $(xenstore-list "$base_path/$dom")
96 do
97 d=$(xenstore_read_default "$base_path/$dom/$dev/physical-device" "")
99 if [ "$d" == "$devmm" ]
100 then
101 if [ "$mode" == 'w' ]
102 then
103 if ! same_vm $dom
104 then
105 echo 'guest'
106 return
107 fi
108 else
109 local m=$(xenstore_read "$base_path/$dom/$dev/mode")
110 m=$(canonicalise_mode "$m")
112 if [ "$m" == 'w' ]
113 then
114 if ! same_vm $dom
115 then
116 echo 'guest'
117 return
118 fi
119 fi
120 fi
121 fi
122 done
123 done
125 echo 'ok'
126 }
129 same_vm()
130 {
131 local otherdom="$1"
132 # Note that othervm can be MISSING here, because Xend will be racing with
133 # the hotplug scripts -- the entries in /local/domain can be removed by
134 # Xend before the hotplug scripts have removed the entry in
135 # /local/domain/0/backend/. In this case, we want to pretend that the
136 # VM is the same as FRONTEND_UUID, because that way the 'sharing' will be
137 # allowed.
138 local othervm=$(xenstore_read_default "/local/domain/$otherdom/vm" \
139 "$FRONTEND_UUID")
141 [ "$FRONTEND_UUID" == "$othervm" ]
142 }
145 ##
146 # check_device_sharing dev mode
147 #
148 # Perform the sharing check for the given physical device and mode.
149 #
150 check_device_sharing()
151 {
152 local dev="$1"
153 local mode=$(canonicalise_mode "$2")
154 local result
156 if [ "$mode" == '!' ]
157 then
158 return 0
159 fi
161 result=$(check_sharing "$dev" "$mode")
163 if [ "$result" != 'ok' ]
164 then
165 do_ebusy "Device $dev is mounted " "$mode" "$result"
166 fi
167 }
170 ##
171 # check_device_sharing file dev mode
172 #
173 # Perform the sharing check for the given file mounted through the given
174 # loopback interface, in the given mode.
175 #
176 check_file_sharing()
177 {
178 local file="$1"
179 local dev="$2"
180 local mode="$3"
182 result=$(check_sharing "$dev" "$mode")
184 if [ "$result" != 'ok' ]
185 then
186 do_ebusy "File $file is loopback-mounted through $dev,
187 which is mounted " "$mode" "$result"
188 fi
189 }
192 ##
193 # do_ebusy prefix mode result
194 #
195 # Helper function for check_device_sharing check_file_sharing, calling ebusy
196 # with an error message constructed from the given prefix, mode, and result
197 # from a call to check_sharing.
198 #
199 do_ebusy()
200 {
201 local prefix="$1"
202 local mode="$2"
203 local result="$3"
205 if [ "$result" == 'guest' ]
206 then
207 dom='a guest '
208 when='now'
209 else
210 dom='the privileged '
211 when='by a guest'
212 fi
214 if [ "$mode" == 'w' ]
215 then
216 m1=''
217 m2=''
218 else
219 m1='read-write '
220 m2='read-only '
221 fi
223 release_lock "block"
224 ebusy \
225 "${prefix}${m1}in ${dom}domain,
226 and so cannot be mounted ${m2}${when}."
227 }
230 t=$(xenstore_read_default "$XENBUS_PATH/type" 'MISSING')
232 case "$command" in
233 add)
234 phys=$(xenstore_read_default "$XENBUS_PATH/physical-device" 'MISSING')
235 if [ "$phys" != 'MISSING' ]
236 then
237 # Depending upon the hotplug configuration, it is possible for this
238 # script to be called twice, so just bail.
239 exit 0
240 fi
242 p=$(xenstore_read "$XENBUS_PATH/params")
243 mode=$(xenstore_read "$XENBUS_PATH/mode")
245 case $t in
246 phy)
247 dev=$(expand_dev $p)
248 FRONTEND_ID=$(xenstore_read "$XENBUS_PATH/frontend-id")
249 FRONTEND_UUID=$(xenstore_read_default \
250 "/local/domain/$FRONTEND_ID/vm" 'unknown')
251 claim_lock "block"
252 check_device_sharing "$dev" "$mode"
253 write_dev "$dev"
254 release_lock "block"
255 exit 0
256 ;;
258 file)
259 # Canonicalise the file, for sharing check comparison, and the mode
260 # for ease of use here.
261 file=$(readlink -f "$p") || fatal "$p does not exist."
262 mode=$(canonicalise_mode "$mode")
264 claim_lock "block"
266 if [ "$mode" == 'w' ] && ! stat "$file" -c %A | grep -q w
267 then
268 release_lock "block"
269 ebusy \
270 "File $file is read-only, and so I will not
271 mount it read-write in a guest domain."
272 fi
274 loopdev=''
275 for dev in /dev/loop*
276 do
277 if [ ! -b "$dev" ]
278 then
279 continue
280 fi
282 f=$(losetup "$dev" 2>/dev/null) || f=''
284 if [ "$f" ]
285 then
286 # $dev is in use. Check sharing.
287 if [ "$mode" == '!' ]
288 then
289 continue
290 fi
292 f=$(echo "$f" | sed -e 's/.*(\(.*\)).*/\1/g')
294 # $f is the filename, as read from losetup, but the loopback
295 # driver truncates filenames at 64 characters, so we need to go
296 # trawling through the store if it's longer than that. Truncation
297 # is indicated by an asterisk at the end of the filename.
298 if expr index "$f" '*' >/dev/null
299 then
300 found=""
301 for dom in $(xenstore-list "$XENBUS_BASE_PATH")
302 do
303 for domdev in $(xenstore-list "$XENBUS_BASE_PATH/$dom")
304 do
305 d=$(xenstore_read_default \
306 "$XENBUS_BASE_PATH/$dom/$domdev/node" "")
307 if [ "$d" == "$dev" ]
308 then
309 f=$(xenstore_read "$XENBUS_BASE_PATH/$dom/$domdev/params")
310 found=1
311 break 2
312 fi
313 done
314 done
316 if [ ! "$found" ]
317 then
318 # This loopback device is in use by someone else, so skip it.
319 log debug "Loopback sharing check skips device $dev."
320 continue
321 fi
322 fi
324 # Canonicalise the filename for the comparison.
326 # I have seen this readlink fails because the filename given by
327 # losetup is only the basename. This cannot happen when the loop
328 # device is set up through this script, because file is
329 # canonicalised above, but it may happen when loop devices are set
330 # up some other way. This readlink may also conceivably fail if
331 # the file backing this loop device has been removed.
333 # For maximum safety, in the case that $f does not resolve, we
334 # assume that $file and $f are in the same directory.
336 # If you create a loopback filesystem, remove it and continue to
337 # run on it, and then create another file with the same name, then
338 # this check will block that -- don't do that.
340 # If you create loop devices through some other mechanism, use
341 # relative filenames, and then use the same filename through this
342 # script, then this check will block that -- don't do that either.
344 f=$(readlink -f "$f" || echo $(dirname "$file")/$(basename "$f"))
347 if [ "$f" == "$file" ]
348 then
349 check_file_sharing "$file" "$dev" "$mode"
350 fi
351 else
352 # $dev is not in use, so we'll remember it for use later; we want
353 # to finish the sharing check first.
355 if [ "$loopdev" == '' ]
356 then
357 loopdev="$dev"
358 fi
359 fi
360 done
362 if [ "$loopdev" == '' ]
363 then
364 fatal 'Failed to find an unused loop device'
365 fi
367 do_or_die losetup "$loopdev" "$file"
368 xenstore_write "$XENBUS_PATH/node" "$loopdev"
369 write_dev "$loopdev"
370 release_lock "block"
371 exit 0
372 ;;
373 esac
374 ;;
376 remove)
377 case $t in
378 phy)
379 exit 0
380 ;;
382 file)
383 node=$(xenstore_read "$XENBUS_PATH/node")
384 losetup -d "$node"
385 exit 0
386 ;;
387 esac
388 ;;
390 esac
392 # If we've reached here, $t is neither phy nor file, so fire a helper script.
393 [ -x /etc/xen/scripts/block-"$t" ] && \
394 /etc/xen/scripts/block-"$t" "$command" $node