ia64/xen-unstable

view tools/examples/block @ 16642:643ab64d12d5

tools/examples/block better use of losetup -r

Use losetup -r when the vbd is specified to be readonly, but only if
-r is supported (rather than always creating a writeable losetup
mapping if possible).

This was inspired by (but not derived from) a diff from the Fedora 8
patchset which uses -r iff the vbd is specified as readonly. We need
to be cleverer upstream because not all systems have losetup -r.

Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Dec 19 15:05:15 2007 +0000 (2007-12-19)
parents 46af6ec3ae4e
children 78a29cf8476b
line source
1 #!/bin/bash
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 # check_sharing device mode
22 #
23 # Check whether the device requested is already in use. To use the device in
24 # read-only mode, it may be in use in read-only mode, but may not be in use in
25 # read-write anywhere at all. To use the device in read-write mode, it must
26 # not be in use anywhere at all.
27 #
28 # Prints one of
29 #
30 # 'local': the device may not be used because it is mounted in the current
31 # (i.e. the privileged domain) in a way incompatible with the
32 # requested mode;
33 # 'guest': the device may not be used because it already mounted by a guest
34 # in a way incompatible with the requested mode; or
35 # 'ok': the device may be used.
36 #
37 check_sharing()
38 {
39 local dev="$1"
40 local mode="$2"
42 local devmm=$(device_major_minor "$dev")
43 local file
45 if [ "$mode" = 'w' ]
46 then
47 toskip="^$"
48 else
49 toskip="^[^ ]* [^ ]* [^ ]* ro[, ]"
50 fi
52 for file in $(cat /proc/mounts | grep -v "$toskip" | cut -f 1 -d ' ')
53 do
54 if [ -e "$file" ]
55 then
56 local d=$(device_major_minor "$file")
58 if [ "$d" = "$devmm" ]
59 then
60 echo 'local'
61 return
62 fi
63 fi
64 done
66 local base_path="$XENBUS_BASE_PATH/$XENBUS_TYPE"
67 for dom in $(xenstore-list "$base_path")
68 do
69 for dev in $(xenstore-list "$base_path/$dom")
70 do
71 d=$(xenstore_read_default "$base_path/$dom/$dev/physical-device" "")
73 if [ "$d" = "$devmm" ]
74 then
75 if [ "$mode" = 'w' ]
76 then
77 if ! same_vm $dom
78 then
79 echo 'guest'
80 return
81 fi
82 else
83 local m=$(xenstore_read "$base_path/$dom/$dev/mode")
84 m=$(canonicalise_mode "$m")
86 if [ "$m" = 'w' ]
87 then
88 if ! same_vm $dom
89 then
90 echo 'guest'
91 return
92 fi
93 fi
94 fi
95 fi
96 done
97 done
99 echo 'ok'
100 }
103 ##
104 # check_device_sharing dev mode
105 #
106 # Perform the sharing check for the given physical device and mode.
107 #
108 check_device_sharing()
109 {
110 local dev="$1"
111 local mode=$(canonicalise_mode "$2")
112 local result
114 if [ "x$mode" = 'x!' ]
115 then
116 return 0
117 fi
119 result=$(check_sharing "$dev" "$mode")
121 if [ "$result" != 'ok' ]
122 then
123 do_ebusy "Device $dev is mounted " "$mode" "$result"
124 fi
125 }
128 ##
129 # check_device_sharing file dev mode
130 #
131 # Perform the sharing check for the given file mounted through the given
132 # loopback interface, in the given mode.
133 #
134 check_file_sharing()
135 {
136 local file="$1"
137 local dev="$2"
138 local mode="$3"
140 result=$(check_sharing "$dev" "$mode")
142 if [ "$result" != 'ok' ]
143 then
144 do_ebusy "File $file is loopback-mounted through $dev,
145 which is mounted " "$mode" "$result"
146 fi
147 }
150 ##
151 # do_ebusy prefix mode result
152 #
153 # Helper function for check_device_sharing check_file_sharing, calling ebusy
154 # with an error message constructed from the given prefix, mode, and result
155 # from a call to check_sharing.
156 #
157 do_ebusy()
158 {
159 local prefix="$1"
160 local mode="$2"
161 local result="$3"
163 if [ "$result" = 'guest' ]
164 then
165 dom='a guest '
166 when='now'
167 else
168 dom='the privileged '
169 when='by a guest'
170 fi
172 if [ "$mode" = 'w' ]
173 then
174 m1=''
175 m2=''
176 else
177 m1='read-write '
178 m2='read-only '
179 fi
181 release_lock "block"
182 ebusy \
183 "${prefix}${m1}in ${dom}domain,
184 and so cannot be mounted ${m2}${when}."
185 }
188 t=$(xenstore_read_default "$XENBUS_PATH/type" 'MISSING')
190 case "$command" in
191 add)
192 phys=$(xenstore_read_default "$XENBUS_PATH/physical-device" 'MISSING')
193 if [ "$phys" != 'MISSING' ]
194 then
195 # Depending upon the hotplug configuration, it is possible for this
196 # script to be called twice, so just bail.
197 exit 0
198 fi
200 if [ -n "$t" ]
201 then
202 p=$(xenstore_read "$XENBUS_PATH/params")
203 mode=$(xenstore_read "$XENBUS_PATH/mode")
204 fi
206 case $t in
207 phy)
208 dev=$(expand_dev $p)
209 FRONTEND_ID=$(xenstore_read "$XENBUS_PATH/frontend-id")
210 FRONTEND_UUID=$(xenstore_read_default \
211 "/local/domain/$FRONTEND_ID/vm" 'unknown')
212 claim_lock "block"
213 check_device_sharing "$dev" "$mode"
214 write_dev "$dev"
215 release_lock "block"
216 exit 0
217 ;;
219 file)
220 # Canonicalise the file, for sharing check comparison, and the mode
221 # for ease of use here.
222 file=$(readlink -f "$p") || fatal "$p does not exist."
223 test -f "$file" || fatal "$file does not exist."
224 mode=$(canonicalise_mode "$mode")
226 claim_lock "block"
228 if [ "$mode" = 'w' ] && ! stat "$file" -c %A | grep -q w
229 then
230 release_lock "block"
231 ebusy \
232 "File $file is read-only, and so I will not
233 mount it read-write in a guest domain."
234 fi
236 loopdev=''
237 for dev in /dev/loop*
238 do
239 if [ ! -b "$dev" ]
240 then
241 continue
242 fi
244 f=$(losetup "$dev" 2>/dev/null) || f=''
246 if [ "$f" ]
247 then
248 # $dev is in use. Check sharing.
249 if [ "x$mode" = 'x!' ]
250 then
251 continue
252 fi
254 f=$(echo "$f" | sed -e 's/.*(\(.*\)).*/\1/g')
256 # $f is the filename, as read from losetup, but the loopback
257 # driver truncates filenames at 64 characters, so we need to go
258 # trawling through the store if it's longer than that. Truncation
259 # is indicated by an asterisk at the end of the filename.
260 if expr index "$f" '*' >/dev/null
261 then
262 found=""
263 for dom in $(xenstore-list "$XENBUS_BASE_PATH")
264 do
265 for domdev in $(xenstore-list "$XENBUS_BASE_PATH/$dom")
266 do
267 d=$(xenstore_read_default \
268 "$XENBUS_BASE_PATH/$dom/$domdev/node" "")
269 if [ "$d" = "$dev" ]
270 then
271 f=$(xenstore_read "$XENBUS_BASE_PATH/$dom/$domdev/params")
272 found=1
273 break 2
274 fi
275 done
276 done
278 if [ ! "$found" ]
279 then
280 # This loopback device is in use by someone else, so skip it.
281 log debug "Loopback sharing check skips device $dev."
282 continue
283 fi
284 fi
286 # Canonicalise the filename for the comparison.
288 # I have seen this readlink fails because the filename given by
289 # losetup is only the basename. This cannot happen when the loop
290 # device is set up through this script, because file is
291 # canonicalised above, but it may happen when loop devices are set
292 # up some other way. This readlink may also conceivably fail if
293 # the file backing this loop device has been removed.
295 # For maximum safety, in the case that $f does not resolve, we
296 # assume that $file and $f are in the same directory.
298 # If you create a loopback filesystem, remove it and continue to
299 # run on it, and then create another file with the same name, then
300 # this check will block that -- don't do that.
302 # If you create loop devices through some other mechanism, use
303 # relative filenames, and then use the same filename through this
304 # script, then this check will block that -- don't do that either.
306 f=$(readlink -f "$f" || echo $(dirname "$file")/$(basename "$f"))
309 if [ "$f" = "$file" ]
310 then
311 check_file_sharing "$file" "$dev" "$mode"
312 fi
313 else
314 # $dev is not in use, so we'll remember it for use later; we want
315 # to finish the sharing check first.
317 if [ "$loopdev" = '' ]
318 then
319 loopdev="$dev"
320 fi
321 fi
322 done
324 if [ "$loopdev" = '' ]
325 then
326 release_lock "block"
327 fatal 'Failed to find an unused loop device'
328 fi
330 if LANG=C losetup -h 2>&1 | grep read-only >/dev/null
331 then
332 roflag="-$mode"; roflag="${roflag#-w}"
333 else
334 roflag=''
335 fi
336 do_or_die losetup $roflag "$loopdev" "$file"
337 xenstore_write "$XENBUS_PATH/node" "$loopdev"
338 write_dev "$loopdev"
339 release_lock "block"
340 exit 0
341 ;;
343 "")
344 claim_lock "block"
345 success
346 release_lock "block"
347 ;;
348 esac
349 ;;
351 remove)
352 case $t in
353 phy)
354 exit 0
355 ;;
357 file)
358 node=$(xenstore_read "$XENBUS_PATH/node")
359 losetup -d "$node"
360 exit 0
361 ;;
363 "")
364 exit 0
365 ;;
366 esac
367 ;;
369 esac
371 # If we've reached here, $t is neither phy nor file, so fire a helper script.
372 [ -x /etc/xen/scripts/block-"$t" ] && \
373 /etc/xen/scripts/block-"$t" "$command" $node