source: trunk/packages/xen-3.1/xen-3.1/tools/examples/block @ 34

Last change on this file since 34 was 34, checked in by hartmans, 17 years ago

Add xen and xen-common

File size: 9.6 KB
Line 
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
19
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#
30canonicalise_mode()
31{
32  local mode="$1"
33
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}
44
45
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#
63check_sharing()
64{
65  local dev="$1"
66  local mode="$2"
67
68  local devmm=$(device_major_minor "$dev")
69  local file
70
71  if [ "$mode" = 'w' ]
72  then
73    toskip="^$"
74  else
75    toskip="^[^ ]* [^ ]* [^ ]* ro[, ]"
76  fi
77
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")
83
84      if [ "$d" = "$devmm" ]
85      then
86        echo 'local'
87        return
88      fi
89    fi
90  done
91
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" "")
98
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")
111
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
124
125  echo 'ok'
126}
127
128
129same_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")
140
141  [ "$FRONTEND_UUID" = "$othervm" ]
142}
143
144
145##
146# check_device_sharing dev mode
147#
148# Perform the sharing check for the given physical device and mode.
149#
150check_device_sharing()
151{
152  local dev="$1"
153  local mode=$(canonicalise_mode "$2")
154  local result
155
156  if [ "x$mode" = 'x!' ]
157  then
158    return 0
159  fi
160
161  result=$(check_sharing "$dev" "$mode")
162
163  if [ "$result" != 'ok' ]
164  then
165    do_ebusy "Device $dev is mounted " "$mode" "$result"
166  fi
167}
168
169
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#
176check_file_sharing()
177{
178  local file="$1"
179  local dev="$2"
180  local mode="$3"
181
182  result=$(check_sharing "$dev" "$mode")
183
184  if [ "$result" != 'ok' ]
185  then
186    do_ebusy "File $file is loopback-mounted through $dev,
187which is mounted " "$mode" "$result"
188  fi
189}
190
191
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#
199do_ebusy()
200{
201  local prefix="$1"
202  local mode="$2"
203  local result="$3"
204
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
213
214  if [ "$mode" = 'w' ]
215  then
216    m1=''
217    m2=''
218  else
219    m1='read-write '
220    m2='read-only '
221  fi
222
223  release_lock "block"
224  ebusy \
225"${prefix}${m1}in ${dom}domain,
226and so cannot be mounted ${m2}${when}."
227}
228
229
230t=$(xenstore_read_default "$XENBUS_PATH/type" 'MISSING')
231
232case "$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
241
242    if [ -n "$t" ]
243    then
244      p=$(xenstore_read "$XENBUS_PATH/params")
245      mode=$(xenstore_read "$XENBUS_PATH/mode")
246    fi
247
248    case $t in
249      phy)
250        dev=$(expand_dev $p)
251        FRONTEND_ID=$(xenstore_read "$XENBUS_PATH/frontend-id")
252        FRONTEND_UUID=$(xenstore_read_default \
253            "/local/domain/$FRONTEND_ID/vm" 'unknown')
254        claim_lock "block"
255        check_device_sharing "$dev" "$mode"
256        write_dev "$dev"
257        release_lock "block"
258        exit 0
259        ;;
260
261      file)
262        # Canonicalise the file, for sharing check comparison, and the mode
263        # for ease of use here.
264        file=$(readlink -f "$p") || fatal "$p does not exist."
265        mode=$(canonicalise_mode "$mode")
266
267        claim_lock "block"
268
269        if [ "$mode" = 'w' ] && ! stat "$file" -c %A | grep -q w
270        then
271          release_lock "block"
272          ebusy \
273"File $file is read-only, and so I will not
274mount it read-write in a guest domain."
275        fi
276
277        loopdev=''
278        for dev in /dev/loop*
279        do
280          if [ ! -b "$dev" ]
281          then
282            continue
283          fi
284
285          f=$(losetup "$dev" 2>/dev/null) || f=''
286
287          if [ "$f" ]
288          then
289            # $dev is in use.  Check sharing.
290            if [ "x$mode" = 'x!' ]
291            then
292              continue
293            fi
294
295            f=$(echo "$f" | sed -e 's/.*(\(.*\)).*/\1/g')
296
297            # $f is the filename, as read from losetup, but the loopback
298            # driver truncates filenames at 64 characters, so we need to go
299            # trawling through the store if it's longer than that.  Truncation
300            # is indicated by an asterisk at the end of the filename.
301            if expr index "$f" '*' >/dev/null
302            then
303              found=""
304              for dom in $(xenstore-list "$XENBUS_BASE_PATH")
305              do
306                for domdev in $(xenstore-list "$XENBUS_BASE_PATH/$dom")
307                do
308                  d=$(xenstore_read_default \
309                        "$XENBUS_BASE_PATH/$dom/$domdev/node" "")
310                  if [ "$d" = "$dev" ]
311                  then
312                    f=$(xenstore_read "$XENBUS_BASE_PATH/$dom/$domdev/params")
313                    found=1
314                    break 2
315                  fi
316                done
317              done
318
319              if [ ! "$found" ]
320              then
321                # This loopback device is in use by someone else, so skip it.
322                log debug "Loopback sharing check skips device $dev."
323                continue
324              fi
325            fi
326
327            # Canonicalise the filename for the comparison.
328
329            # I have seen this readlink fails because the filename given by
330            # losetup is only the basename.  This cannot happen when the loop
331            # device is set up through this script, because file is
332            # canonicalised above, but it may happen when loop devices are set
333            # up some other way.  This readlink may also conceivably fail if
334            # the file backing this loop device has been removed.
335
336            # For maximum safety, in the case that $f does not resolve, we
337            # assume that $file and $f are in the same directory.
338
339            # If you create a loopback filesystem, remove it and continue to
340            # run on it, and then create another file with the same name, then
341            # this check will block that -- don't do that.
342
343            # If you create loop devices through some other mechanism, use
344            # relative filenames, and then use the same filename through this
345            # script, then this check will block that -- don't do that either.
346
347            f=$(readlink -f "$f" || echo $(dirname "$file")/$(basename "$f"))
348
349
350            if [ "$f" = "$file" ]
351            then
352              check_file_sharing "$file" "$dev" "$mode"
353            fi
354          else
355            # $dev is not in use, so we'll remember it for use later; we want
356            # to finish the sharing check first.
357
358            if [ "$loopdev" = '' ]
359            then
360              loopdev="$dev"
361            fi
362          fi
363        done
364
365        if [ "$loopdev" = '' ]
366        then
367          fatal 'Failed to find an unused loop device'
368        fi
369
370        do_or_die losetup "$loopdev" "$file"
371        xenstore_write "$XENBUS_PATH/node" "$loopdev"
372        write_dev "$loopdev"
373        release_lock "block"
374        exit 0
375        ;;
376
377      "")
378        claim_lock "block"
379        success
380        release_lock "block"
381        ;;
382    esac
383    ;;
384
385  remove)
386    case $t in
387      phy)
388        exit 0
389        ;;
390
391      file)
392        node=$(xenstore_read "$XENBUS_PATH/node")
393        losetup -d "$node"
394        exit 0
395        ;;
396
397      "")
398        exit 0
399        ;;
400    esac
401    ;;
402
403esac
404
405# If we've reached here, $t is neither phy nor file, so fire a helper script.
406[ -x /etc/xen/scripts/block-"$t" ] && \
407  /etc/xen/scripts/block-"$t" "$command" $node
Note: See TracBrowser for help on using the repository browser.