試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

QEMUのrawディスクイメージに関する覚え書きの追加

QEMUのrawディスクイメージに関する覚え書き(後半)」でホストOSからハードディスクのrawイメージファイル内のパーティションにアクセスしているが、幾つかの内容を追加。

マウントとマウント解除を自動化

QEMUのrawディスクイメージに関する覚え書き(後半)」の「rawイメージの中にアクセスする」でホストOSからrawディスクイメージ内のパーティションをマウントする作業は、開始位置やマウントポイント、LVMのボリュームグループや論理ボリューム名といった情報があれば決まった流れとなるため、中身にアクセスすることが多ければスクリプトで自動化してしまうと楽。
下のスクリプトを使用する場合、上のほうにある幾つかの変数を環境とディスクイメージの内容に合わせて設定して使う。

LVMを使用しない場合のスクリプト

[任意]ファイル名: mount-rawdisk.sh

#! /bin/bash

# rawファイル
RAWFILE=~/image.raw
# マウントしたいパーティションの情報
MOUNTPOINT=/media/rawdisk
MOUNTOPT="-o ro,loop"
#MOUNTOPT="-o loop"
TYPE=ext2
# 「/sbin/fdisk -lu [デバイス名]」で見たときの開始位置を記述
START=63





if [[ ! -d "${MOUNTPOINT}" ]]; then
  zenity --error --title "マウントポイントが存在しません" --text "ディレクトリ \"${MOUNTPOINT}\"
存在しないため、マウントすることができません"
  exit 1
fi

start()
{
  local loopdev=$(/sbin/losetup -f)
  if [[ ${?} -ne 0 ]]; then
    zenity --error --title "ループバックデバイスが使用できません" --text "使用できるループバックデバイスがないため
  イメージの中身にアクセスできません"
    exit 1
  fi
  # fdiskを実行するために先頭から接続
  sudo /sbin/losetup ${loopdev} "${RAWFILE}"
  # 単位の取得(512など)
  UNIT=$(LC_ALL=C /sbin/fdisk -lu ${loopdev} | awk '/Unit/ { print $7; }')
  # 切断
  sudo /sbin/losetup -d ${loopdev}

  OFFSET=$((${START} * ${UNIT}))

  # マウント
  if ! sudo mount -t ${TYPE} ${MOUNTOPT},offset=${OFFSET} "${RAWFILE}" "${MOUNTPOINT}"; then
    zenity --error --title "マウント失敗" --text "マウントに失敗しました"
    exit 1
  fi

  exit 0
}

stop()
{
  # マウント解除
  if ! sudo umount "${MOUNTPOINT}"; then
    zenity --error --title "マウント解除失敗" --text "マウント解除に失敗しました"
    exit 1
  fi
}

case "${1}" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  *)
    echo "Usage: $(basename "${0}") {start|stop}"
    exit 1
esac
LVMを使用した場合のスクリプト

[任意]ファイル名: mount-rawdisk-lvm.sh

#! /bin/bash

# rawファイル
RAWFILE=~/image.raw
# マウントしたいパーティションの情報
LOGVOL=LogVol
VOLGROUP=VolGroup
MOUNTPOINT=/media/rawdisk
MOUNTOPT="-o ro"
#MOUNTOPT=""
TYPE=ext3
# 「/sbin/fdisk -lu [デバイス名]」で見たときの開始位置を記述
START=208845





if [[ ! -d "${MOUNTPOINT}" ]]; then
  zenity --error --title "マウントポイントが存在しません" --text "ディレクトリ \"${MOUNTPOINT}\"
存在しないため、マウントすることができません"
  exit 1
fi

start()
{
  local loopdev=$(/sbin/losetup -f)
  if [[ ${?} -ne 0 ]]; then
    zenity --error --title "ループバックデバイスが使用できません" --text "使用できるループバックデバイスがないため
  イメージの中身にアクセスできません"
    exit 1
  fi
  # fdiskを実行するために先頭から接続
  sudo /sbin/losetup ${loopdev} "${RAWFILE}"
  # 単位の取得(512など)
  UNIT=$(LC_ALL=C /sbin/fdisk -lu ${loopdev} | awk '/Unit/ { print $7; }')
  # 一旦切断
  sudo /sbin/losetup -d ${loopdev}
  # 再び接続
  loopdev=$(/sbin/losetup -f)
  if [[ ${?} -ne 0 ]]; then
    zenity --error --title "ループバックデバイスが使用できません" --text "ループバックデバイスが使用できないため
  イメージの中身にアクセスできません"
    exit 1
  fi
  OFFSET=$((${START} * ${UNIT}))
  sudo /sbin/losetup -o ${OFFSET} ${loopdev} "${RAWFILE}"

  sudo /sbin/pvscan >/dev/null
  if ! sudo /sbin/lvscan | grep "/dev/${VOLGROUP}/${LOGVOL}" >/dev/null; then
    # 論理ボリュームが見つからない
    zenity --error --title "論理ボリュームが見つかりません" --text "デバイス
\"/dev/${VOLGROUP}/${LOGVOL}\"
が見つからないため、マウントできません"
    exit 1
  fi

  # アクティブ化
  sudo /sbin/lvchange -a y "/dev/${VOLGROUP}/${LOGVOL}"
  # マウント
  if ! sudo mount -t ${TYPE} ${MOUNTOPT} "/dev/${VOLGROUP}/${LOGVOL}" "${MOUNTPOINT}"; then
    zenity --error --title "マウント失敗" --text "マウントに失敗しました"
    exit 1
  fi

  exit 0
}

stop()
{
  local loopdev=$(/sbin/losetup -a | grep ${RAWFILE} | awk -F : '{ print $1; }' | head -n 1)
  # マウント解除
  if ! sudo umount "${MOUNTPOINT}"; then
    zenity --error --title "マウント解除失敗" --text "マウント解除に失敗しました"
    exit 1
  fi
  # 非アクティブ化
  sudo /sbin/lvchange -a n "/dev/${VOLGROUP}/${LOGVOL}"
  # ループバックデバイスとイメージを切断
  sudo /sbin/losetup -d ${loopdev}
}

case "${1}" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  *)
    echo "Usage: $(basename "${0}") {start|stop}"
    exit 1
esac

ホストOSからパーティションを操作することはできるか?

GPartedの引数に/dev/loop[番号]を渡して起動すると、ホストOS上でグラフィカルにパーティション構成を確認することはできるが

/dev/loop[番号]p[番号]というデバイス/dev/以下には存在しないため、操作を行うと失敗することがある。

スワップパーティションを一度消して(これは成功)、同じ領域をスワップとして初期化しようとしたところ失敗した。
一方で、losetupパーティションをoffset指定で接続して手動でmkswapした場合はどうなるかを実験したところ

$ LOOPDEV=$(/sbin/losetup -f); [[ ${?} -eq 0 ]] && sudo /sbin/losetup ${LOOPDEV} [rawファイル]
$ /sbin/fdisk -lu ${LOOPDEV} 

Disk /dev/loop4: 6442 MB, 6442450944 bytes
255 heads, 63 sectors/track, 783 cylinders, total 12582912 sectors
Units = セクタ数 of 1 * 512 = 512 bytes
Disk identifier: 0x000836ef

デバイス Boot      Start         End      Blocks   Id  System
/dev/loop4p1   *          63    11743514     5871726   83  Linux
/dev/loop4p2        11743515    12578894      417690    5  拡張領域
/dev/loop4p5        11743578    12578894      417658+  82  Linux swap / Solaris

このような構成で

$ /sbin/losetup -d ${LOOPDEV}
$ LOOPDEV=$(/sbin/losetup -f); [[ ${?} -eq 0 ]] && sudo /sbin/losetup -o $((11743578 * 512)) ${LOOPDEV} [rawファイル]

5番のパーティションに接続後

$ sudo mkswap -L sw ${LOOPDEV}
Setting up swapspace version 1, size = 429731 kB
LABEL=sw, UUID=db5864ec-365c-4e85-9b44-c45de796f336
$ sudo /sbin/losetup -d ${LOOPDEV}; unset ${LOOPDEV}

スワップの初期化を行ったのだが、ゲストOS上で有効にしようとしたところ

guest# swapon /dev/sda5
swapon: /dev/sda5: Invalid argument

失敗してしまった。スワップの代わりにmkfs.ext2をこの領域に対して(/dev/loop[番号]に対して)実行したところ、ゲストOS上でマウントができ、データのやりとりもできた(作業内容は省略)。
しかし、このような作業は危険な上にメリットも少ないため、操作を確実に行いたいのであれば、ライブCDを使用して仮想マシン上で作業を行うのが無難。

関連記事: