qemu-mount - Tools for mounting QEMU images
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

156 lines
3.7 KiB

  1. #!/bin/sh
  2. #
  3. # mount qemu image file into a directory
  4. #
  5. prog=$(basename "$0")
  6. # use sudo for unprivileged users
  7. SUDO=
  8. [ "$(id -u)" -ne 0 ] && SUDO=sudo
  9. # print error message
  10. error() {
  11. echo "$prog:" "$@" 2>&1
  12. exit 1
  13. }
  14. # print usage
  15. usage() {
  16. if [ $# -ge 1 ]; then
  17. [ -n "$*" ] && echo "$prog:" "$@" 2>&1
  18. echo 2>&1
  19. fi
  20. cat << EOF >&2
  21. Mount the qemu image file into a directory.
  22. Usage: $prog [OPTIONS] qemu_image mount_point
  23. Options:
  24. -p partition_number select, which partition to mount, default #1
  25. -r mount read-only
  26. -t fstype set filesystem type
  27. -o mount_options additional mount options
  28. -u set owner to current user
  29. Without arguments it prints a list of mounted images.
  30. EOF
  31. exit 1
  32. }
  33. # Without arguments: print mounted images
  34. if [ $# -eq 0 ]; then
  35. for file in /tmp/nbd*.mount; do
  36. nbd=$(basename "$file" .mount)
  37. [ "$nbd" = "nbd*" ] && exit 0
  38. image=
  39. while read -r line; do
  40. if [ -z "$image" ]; then
  41. image="$line"
  42. else
  43. mdir="$line"
  44. echo "$nbd: $image -> $mdir"
  45. break
  46. fi
  47. done < "$file"
  48. done
  49. exit 0
  50. fi
  51. # parse command line
  52. partition=1
  53. read_only=
  54. fstype=
  55. mnt_options=
  56. set_uid=
  57. while getopts ":p:rt:o:u" opt; do
  58. case $opt in
  59. p) partition="$OPTARG"
  60. echo "$partition" | grep -q -x "[0-9][0-9]*" || \
  61. error "partition number must be numeric" ;;
  62. r) read_only=1 ;;
  63. t) fstype=$OPTARG ;;
  64. o) mnt_options="${mnt_options},${OPTARG}" ;;
  65. u) set_uid=1 ;;
  66. \?) usage "illegal option -- $OPTARG" ;;
  67. :) usage "option requires an argument -- $OPTARG" ;;
  68. esac
  69. done
  70. shift $((OPTIND-1)) # remove parsed options and args from $@ list
  71. mnt_options=${mnt_options#,}
  72. [ $# -ne 2 ] && usage "wrong number of arguments"
  73. image="$1"
  74. mdir="$2"
  75. # check file types
  76. [ -e "$image" ] || error "$image: no such file"
  77. [ -f "$image" ] || error "$image must be a regular file"
  78. [ -e "$mdir" ] || error "$mdir: no such directory"
  79. [ -d "$mdir" ] || error "$mdir must be a directory"
  80. full_image=$(readlink -f "$image")
  81. full_mdir=$(readlink -f "$mdir")
  82. img_fmt=$(qemu-img info "$image" | sed -n 's/^file format: \(.*\)/\1/p')
  83. [ -n "$img_fmt" ] || error "$image has unknown format"
  84. # load kernel module, check if sudo works
  85. $SUDO modprobe nbd nbds_max=16 max_part=31 || exit $?
  86. # load FAT filesystem module
  87. $SUDO modprobe vfat 2> /dev/null
  88. # lock
  89. umask=$(umask); umask 0
  90. (
  91. flock -w 5 9 || error "Can't get mount lock"
  92. umask "$umask"
  93. # already in use?
  94. grep -q -F -x "$full_image" /tmp/nbd*.mount 2> /dev/null &&
  95. error "$image already mounted"
  96. grep -q -F -x "$full_mdir" /tmp/nbd*.mount 2> /dev/null &&
  97. error "$mdir already in use"
  98. # find free nbd device
  99. nbd_size=1
  100. for nbd in /sys/class/block/nbd*; do
  101. [ -n "${nbd##*/nbd[0-9]}" ] && \
  102. [ -n "${nbd##*/nbd[0-9][0-9]}" ] && \
  103. continue
  104. nbd_size=$(cat "${nbd}/size")
  105. [ "$nbd_size" -gt 0 ] || break
  106. done
  107. [ "$nbd_size" -gt 0 ] && error "no free nbd devices"
  108. nbd=$(basename "$nbd")
  109. # reserve nbd device
  110. printf "%s\n%s\n" "$full_image" "$full_mdir" > "/tmp/${nbd}.mount" || exit $?
  111. # qemu-nbd
  112. $SUDO qemu-nbd -c "/dev/$nbd" -f "$img_fmt" "$full_image"
  113. ret=$?
  114. if [ $ret -ne 0 ]; then
  115. rm -f "/tmp/${nbd}.mount"
  116. exit $ret
  117. fi
  118. # mount
  119. nbd_dev=/dev/$nbd
  120. [ "$partition" -gt 0 ] && nbd_dev="${nbd_dev}p${partition}"
  121. options=$mnt_options
  122. [ -n "$read_only" ] && options="$options,ro"
  123. [ -n "$set_uid" ] && options="$options,uid=$(id -u),gid=$(id -g)"
  124. options=${options#,}
  125. $SUDO mount ${fstype:+-t "$fstype"} ${options:+-o "$options"} \
  126. "$nbd_dev" "$full_mdir"
  127. ret=$?
  128. if [ $ret -eq 0 ]; then
  129. echo "$prog completed, using device $nbd_dev"
  130. else
  131. sync
  132. $SUDO qemu-nbd -d "/dev/$nbd" > /dev/null 2>&1
  133. rm -f "/tmp/${nbd}.mount"
  134. fi
  135. exit $ret
  136. ) 9>> /var/lock/qemu-mount.lock