HDDイメージファイルをマウントして使う方法

小容量HDDの内容をddコマンドで物理イメージを丸ごとバックアップしたファイルがあった。

[root@linux ~]# dd if=/dev/sdb of=hdd.img bs=512M

このHDDのイメージファイルはLinux上でマウントしてファイルシステムとして利用できる。(追記:この応用としてVMwareの仮想ディスクをホスト上でマウントして使うという応用もある。「VMwareの仮想ディスクをLinuxでマウントして使う」参照。)

mountコマンドのオフセット指定でマウントする

[root@linux ~]# ls /mnt					;: ←/mntの下は空
[root@linux ~]# mount -o loop,offset=31744 hdd.img /mnt	;: ←イメージファイルをマウントし
[root@linux ~]# ls /mnt					:; ←ファイルシステムとして使う
bin   dev  home  lost+found  mnt  poweroff  root  selinux  sys  usr
boot  etc  lib   media       opt  proc      sbin  srv      tmp  var
[root@linux ~]#

ここでオフセットとしてゲタ上げをしているのはマウントしたいパーティションのアドレスになる。HDDの中には複数のパーティションを設定して、それぞれを別々のミニディスクと使えるが、そのパーティションのHDD内での位置(HDDの先頭から何バイトずれたところから始まっているか)である。従って、パーティションのHDD内における位置によって異なる。パーティションの情報はHDDの先頭のMBR(Master Boot Record)という部分に記録されているので、それを解読する必要がある。(MBRに関しては「LinuxにおけるMBRのまとめとバックアップ方法」等を参照。)しかし、解読にはMBRの構造に詳しくないと中々難しいのでパーティションテーブルの情報を確認するためには fdiskコマンドを使う。(fdiskコマンドの拡張命令を使う。)

[root@linux ~]# fdisk hdd.img
You must set cylinders.
You can do this from the extra functions menu.

Command (m for help): x

Expert command (m for help): p

Disk hdd.img: 125 heads, 62 sectors, 0 cylinders

Nr AF  Hd Sec  Cyl  Hd Sec  Cyl     Start      Size ID
 1 80   1   1    0 124  62 1017         62    7889438 83
 2 00   0   0    0   0   0    0          0          0 00
 3 00   0   0    0   0   0    0          0          0 00
 4 00   0   0    0   0   0    0          0          0 00

Expert command (m for help): q

[root@linux ~]#

また、上記のようにインタラクティブモードではなくコマンドモードで各パーティションのスタートセクタとセクタ数を表示できる。

[root@linux ~]# fdisk -l -u  hdd-2.img
You must set cylinders.
You can do this from the extra functions menu.

Disk hdd-2.img: 0 MB, 0 bytes
125 heads, 62 sectors/track, 0 cylinders, total 0 sectors
Units = sectors of 1 * 512 = 512 bytes
Disk identifier: 0x911466d3

    Device Boot      Start         End      Blocks   Id  System
hdd-2.img1              62       61999       30969   83  Linux
hdd-2.img2           62000      123999       31000   83  Linux
hdd-2.img3          124000      247999       62000    5  Extended
hdd-2.img5          124062      185999       30969   83  Linux
hdd-2.img6          186062      247999       30969   83  Linux
[root@linux ~]#

ここで、1セクタ=512バイトなのでStartのセクタに512を掛けた値が各パーティションのHHDの先頭からのオフセット・バイト数となる。例えば、上の例のパーティション6(Startセクタ=185062)をマウントするには次のようにmountを実行する。(電卓で 186062 * 512 を計算してもいいが、以下の例では exprコマンドでの計算結果を直接mountのオプションに設定している。)

[root@linux ~]# mount -o loop,offset=`expr 186062 \* 512` hdd-2.img /mnt
[root@linux ~]# ls /mnt
lost+found
[root@linux ~]#

fdiskを使えば、拡張パーティションも含めて表示できるので、通常はこれを使っていればいいが、パーティション・テーブルの構造を勉強するために、パーティションテーブルの情報をダンプする "partinfo" というコマンド作ってみた。これを使ってオフセットを調べることができる。(プログラムリストは「program list of "partinfo"」を参照。)例えば、パーティション1のオフセットを調べるには次のように実行する。

[root@linux ~]# partinfo -p 1 hdd.img
Partition 1
        Boot Flag       = [80]
        Start Hd/Sc/Cy  = [01/01/000] 1/1/0
        Partition Type  = [83] "Linux native partition"
        End Hd/Sc/Cy    = [7c/3e/3f9] 124/62/1017
        Start LBA       = [0000003e] 62 sectors (31,744 bytes offset)
        Number of LBA   = [0078621e] 7,889,438 sectors (4,039,392,256 bytes)
[root@linux ~]#

これで、パーティション1の始まりはHDDの先頭から 31,744 バイトということが分かるので、mountコマンドのオプションoffsetで指定すればマウントできる。

なお、マウントできるパーティションはmountコマンドが認識できるファイルシステム形式あれば良く、FATやNTFS形式のパーティションもマウントして利用することができる。

kpartxコマンドを使う

fdiskやpartinfoのようなコマンドがあればオフセットを知るのは容易かと思うが、kpartxというコマンド使うことでより容易に(オフセットを計算せずに)任意のパーティションをマウントできる。kpartxはパーティション毎のブロック・デバイス・ファイルを /dev/mapper/の下に生成できる。丁度、ディスク /dev/sdaに対してパーティション1は /dev/sda1、パーティション2は /dev/sda2という風にパーティション毎に別々のデバイスファイルとして扱えるのと同様になる。

kpartxコマンドの使い方は以下の様な感じである。

[root@linux ~]# ls /dev/mapper/				;: ←mapperの下は空(controlだけ)
control
[root@linux ~]# kpartx -v -a hdd-2.img			;: ←kpartxでデバイスを作成
add map loop1p1 : 0 61938 linear /dev/loop1 62
add map loop1p2 : 0 62000 linear /dev/loop1 62000
add map loop1p5 : 0 61938 linear /dev/loop1 124062
add map loop1p6 : 0 61938 linear /dev/loop1 186062
[root@linux ~]# ls /dev/mapper/				;: ←mapperの下に作成されている
control  loop1p1  loop1p2  loop1p5  loop1p6
[root@linux ~]# ls /mnt					;: ←/mntの下が空なのを確認して
[root@linux ~]# mount /dev/mapper/loop1p1 /mnt		;: ←1つのデバイスをマウントする
[root@linux ~]# ls /mnt					;: ←/mntの下にファイルが現れる
lost+found
[root@linux ~]# umount /mnt				;: ←作業が終わったらumountして
[root@linux ~]# kpartx -d hdd-2.img			;: ←kpartxでデバイスを削除
loop deleted : /dev/loop1
[root@linux ~]# ls /dev/mapper/				;: ←mapperの下は再び空に
control
[root@linux ~]#

実用上はパーティション・テーブルからオフセット計算してマウントするよりは、kpartxコマンドを利用して個別のデバイス・ファイルをマウントする方が実際的だろう。ただ、気をつけないと、作業終了時にunmountするだけで、loopデバイスを開放するのを忘れてしまうので注意。

パーティション情報を表示する partinfo

partinfoはMBRや各拡張パーティションにあるパーティションテーブルの情報を表示するためのコマンドである。パーティションテーブルの構造を勉強する目的で作ってみたシェルスクリプトである。デバイス・ファイルやイメージファイルを指定したり、他のコマンドからの標準入力としてのMBR情報を解析して表示する。

[root@linux ~]# partinfo -p 3 hdd-2.img
Partition 3
        Boot Flag       = [00]
        Start Hd/Sc/Cy  = [00/01/010] 0/1/16
        Partition Type  = [05] "DOS 3.3+ Extended Partition"
        End Hd/Sc/Cy    = [7c/3e/01f] 124/62/31
        Start LBA       = [0001e460] 124,000 sector (63,488,000 bytes offset)
        Number of LBA   = [0001e460] 124,000 sectors (63,488,000 bytes)
[root@linux ~]#

とか、

[root@linux ~]# dd if=hdd-2.img bs=512 skip=124000 count=1 | partinfo -p 1
Partition 1
        Boot Flag       = [00]
        Start Hd/Sc/Cy  = [01/01/010] 1/1/16
        Partition Type  = [83] "Linux native partition"
        End Hd/Sc/Cy    = [7c/3e/017] 124/62/23
        Start LBA       = [0000003e] 62 sector (31,744 bytes offset)
        Number of LBA   = [0000f1f2] 61,938 sectors (31,712,256 bytes)
[root@linux ~]#

という風に使う。(2つ目の例は、拡張パーティションパーティションテーブルを参照しているところ。)パーティションを指定する"-p"がなければ全ての基本パーティション(4つ)を表示する。シェルスクリプトなので、特に説明は要らないかと思う。(hexdumpの出力を色々と設定できる、という機能を使っている。)

インストールの方法

以下のプログラム(program list of "partinfo")をコピー&ペーストして、/usr/local/bin/partlistというファイルに保存して、実行権を与えておく。

[root@fedora-8 ~]# cat > /usr/local/bin/partlist
【ブログからコピーしたプログラムをペーストする。最後に Ctrl-D を入力して終了。】
[root@fedora-8 ~]# chmod +x /usr/local/bin/partlist

あとは、実行するだけ。
なお、partlistの引数の処理に "shargs" という別のプログラムを利用している。partlistを利用する場合にはshargsも必要になる。ただ、shargsも同様にシェルスクリプトとして書かれているので、上記と同じ要領で簡単にインストールできる。(shargsについては「シェルスクリプトの引数を処理するためのシェルスクリプト」を参照。プログラムは「program list of "shargs" without line-number」からコピーしてくる。)shargsを /usr/local/bin に置いてあるという前提で partlistは作っているので、もし異なる場所に設置しているのであれば partlistの中の ". /usr/local/bin/shargs"の部分を適宜変える。

program list of "partinfo"

#! /bin/bash

# print Partition Information of HDD MBR
# partinfo Ver. 0.002 (2008/08/08)
# Copyright (C) 2008 Adsaria

# This program is free software; you can redistribute it and/or modify it.
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY.

### Argument handling ##########################################################

ARGS_DEF_opt_char_val="p"
ARGS_DEF_opt_word_flg="help"
ARGS_DEF_opt_word_val="partition"
ARGS_DEF_arg_namelist="device"
ARGS_DEF_arg_num_must=0

. /usr/local/bin/shargs

if [ "${ARGS_error}" == "Y" ]; then echo "${ARGS_usage}" 1>&2 ; exit 1; fi
if [ "${ARGS_opt_help}" == "Y" ]; then echo "${ARGS_usage}" 1>&2 ; exit 1; fi

### Define shell vars ##########################################################

CMD_NAME=`basename "${ARGS_0}"`
HDD="${ARGS__device}"
PARTITION=

if [ -n "${ARGS_opt_p}" -o -n "${ARGS_opt_partition}" ]; then
	if [ -n "${ARGS_opt_p}" ]; then
		PARTITION="${ARGS_opt_p}"
		if [ -n "${ARGS_opt_partition}" ]; then
			if [ "$PARTITION" != "${ARGS_opt_partition}" ]; then
				echo "${CMD_NAME}: partition specification is corrupt." 1>&2
				exit 1
			fi
		fi
	else
		PARTITION="${ARGS_opt_partition}"
	fi
			
fi

TYPE_00='Empty'
TYPE_01='DOS 12-bit FAT'
TYPE_02='XENIX root'
TYPE_03='XENIX /usr'
TYPE_04='DOS 3.0+ 16-bit FAT <32M'
TYPE_05='DOS 3.3+ Extended Partition'
TYPE_06='DOS 3.31+ 16-bit FAT >=32M'
TYPE_07='HPFS/NTFS'
TYPE_08='AIX boot partition'
TYPE_09='AIX data partition'
TYPE_0a='OS/2 Boot Manager'
TYPE_0b='WIN95 OSR2 FAT32'
TYPE_0c='WIN95 OSR2 FAT32, LBA-mapped'
TYPE_0e='WIN95: DOS 16-bit FAT, LBA-mapped'
TYPE_0f='WIN95: Extended partition, LBA-mapped'
TYPE_10='OPUS'
TYPE_11='Hidden DOS 12-bit FAT'
TYPE_12='Configuration/diagnostics partition'
TYPE_14='Hidden DOS 16-bit FAT <32M'
TYPE_16='Hidden DOS 16-bit FAT >=32M'
TYPE_17='Hidden HPFS/NTFS'
TYPE_18='AST SmartSleep Partition'
TYPE_1b='Hidden WIN95 OSR2 FAT32'
TYPE_1c='Hidden WIN95 OSR2 FAT32, LBA-mapped'
TYPE_1e='Hidden WIN95 16-bit FAT, LBA-mapped'
TYPE_24='NEC DOS 3.x'
TYPE_27='Windows RE hidden partition'
TYPE_2a='AtheOS File System (AFS)'
TYPE_2b='SyllableSecure (SylStor)'
TYPE_32='NOS'
TYPE_35='JFS on OS/2 or eCS'
TYPE_38='THEOS ver 3.2 2gb partition'
TYPE_39='Plan 9 partition'
TYPE_39='THEOS ver 4 spanned / Plan 9 partition'
TYPE_3a='THEOS ver 4 4gb partition'
TYPE_3b='THEOS ver 4 extended partition'
TYPE_3c='PartitionMagic recovery partition'
TYPE_3d='Hidden NetWare'
TYPE_40='Venix 80286'
TYPE_41='PPC PReP (Power PC Reference Platform) Boot'
TYPE_42='SFS (Secure Filesystem)'
TYPE_43='Linux native (sharing disk with DRDOS)'
TYPE_44='GoBack partition'
TYPE_45='EUMEL/Elan'
TYPE_46='EUMEL/Elan'
TYPE_47='EUMEL/Elan'
TYPE_48='EUMEL/Elan'
TYPE_4a='Mark Aitchison'\''s ALFS/THIN lightweight filesystem for DOS'
TYPE_4c='Oberon partition'
TYPE_4d='QNX4.x'
TYPE_4e='QNX4.x 2nd part'
TYPE_4f='QNX4.x 3rd part'
TYPE_50='OnTrack Disk Manager (older versions) RO'
TYPE_51='OnTrack Disk Manager RW (DM6 Aux1)'
TYPE_52='CP/M'
TYPE_53='Disk Manager 6.0 Aux3'
TYPE_54='Disk Manager 6.0 Dynamic Drive Overlay (DDO)'
TYPE_55='EZ-Drive'
TYPE_56='Golden Bow VFeature Partitioned Volume.'
TYPE_57='DrivePro'
TYPE_5c='Priam EDisk'
TYPE_61='SpeedStor'
TYPE_63='Unix System V (SCO, ISC Unix, UnixWare, ...), Mach, GNU Hurd'
TYPE_64='Novell Netware 286, 2.xx'
TYPE_65='Novell Netware 386, 3.xx or 4.xx'
TYPE_66='Novell Netware SMS Partition'
TYPE_67='Novell'
TYPE_68='Novell'
TYPE_69='Novell Netware 5+, Novell Netware NSS Partition'
TYPE_70='DiskSecure Multi-Boot'
TYPE_72='V7/x86'
TYPE_74='Scramdisk partition'
TYPE_75='IBM PC/IX'
TYPE_77='M2FS/M2CS partition'
TYPE_77='VNDI Partition'
TYPE_78='XOSL FS'
TYPE_80='MINIX until 1.4a'
TYPE_81='MINIX since 1.4b, early Linux'
TYPE_82='Linux swap'
TYPE_83='Linux native partition'
TYPE_84='OS/2 hidden C: drive'
TYPE_85='Linux extended partition'
TYPE_86='FAT16 volume set'
TYPE_87='NTFS volume set'
TYPE_88='Linux plaintext partition table'
TYPE_8a='Linux Kernel Partition (used by AiR-BOOT)'
TYPE_8b='Legacy Fault Tolerant FAT32 volume'
TYPE_8c='Legacy Fault Tolerant FAT32 volume using BIOS extd INT 13h'
TYPE_8d='Free FDISK hidden Primary DOS FAT12 partitition'
TYPE_8e='Linux Logical Volume Manager partition'
TYPE_90='Free FDISK hidden Primary DOS FAT16 partitition'
TYPE_91='Free FDISK hidden DOS extended partitition'
TYPE_92='Free FDISK hidden Primary DOS large FAT16 partitition'
TYPE_93='Amoeba'
TYPE_94='Amoeba bad block table'
TYPE_95='MIT EXOPC native partitions'
TYPE_97='Free FDISK hidden Primary DOS FAT32 partitition'
TYPE_98='Free FDISK hidden Primary DOS FAT32 partitition (LBA)'
TYPE_98='Datalight ROM-DOS Super-Boot Partition'
TYPE_99='DCE376 logical drive'
TYPE_9a='Free FDISK hidden Primary DOS FAT16 partitition (LBA)'
TYPE_9b='Free FDISK hidden DOS extended partitition (LBA)'
TYPE_9f='BSD/OS'
TYPE_a0='Laptop hibernation partition'
TYPE_a1='HP Volume Expansion (SpeedStor variant)'
TYPE_a3='HP Volume Expansion (SpeedStor variant)'
TYPE_a4='HP Volume Expansion (SpeedStor variant)'
TYPE_a5='BSD/386, 386BSD, NetBSD, FreeBSD'
TYPE_a6='OpenBSD'
TYPE_a7='NeXTStep'
TYPE_a8='Mac OS-X'
TYPE_a9='NetBSD'
TYPE_aa='Olivetti Fat 12 1.44MB Service Partition'
TYPE_ab='Mac OS-X Boot partition'
TYPE_ae='ShagOS filesystem'
TYPE_af='MacOS X HFS'
TYPE_b0='BootStar Dummy'
TYPE_b1='HP Volume Expansion (SpeedStor variant)'
TYPE_b3='HP Volume Expansion (SpeedStor variant)'
TYPE_b4='HP Volume Expansion (SpeedStor variant)'
TYPE_b6='HP Volume Expansion (SpeedStor variant)'
TYPE_b7='BSDI BSD/386 filesystem'
TYPE_b8='BSDI BSD/386 swap partition'
TYPE_bb='Boot Wizard hidden'
TYPE_bc='Acronis backup partition'
TYPE_be='Solaris 8 boot partition'
TYPE_bf='New Solaris x86 partition'
TYPE_c0='DR-DOS/Novell DOS secured partition'
TYPE_c1='DRDOS/secured (FAT-12)'
TYPE_c2='Hidden Linux'
TYPE_c3='Hidden Linux swap'
TYPE_c4='DRDOS/secured (FAT-16, < 32M)'
TYPE_c5='DRDOS/secured (extended)'
TYPE_c6='DRDOS/secured (FAT-16, >= 32M)'
TYPE_c7='Syrinx boot'
TYPE_c8='Reserved for DR-DOS 8.0+'
TYPE_c9='Reserved for DR-DOS 8.0+'
TYPE_ca='Reserved for DR-DOS 8.0+'
TYPE_cb='DR-DOS 7.04+ secured FAT32 (CHS)/'
TYPE_cc='DR-DOS 7.04+ secured FAT32 (LBA)/'
TYPE_cd='CTOS Memdump'
TYPE_ce='DR-DOS 7.04+ FAT16X (LBA)/'
TYPE_cf='DR-DOS 7.04+ secured EXT DOS (LBA)/'
TYPE_d0='REAL/32 secure big partition'
TYPE_d0='Multiuser DOS secured partition'
TYPE_d1='Old Multiuser DOS secured FAT12'
TYPE_d4='Old Multiuser DOS secured FAT16 <32M'
TYPE_d5='Old Multiuser DOS secured extended partition'
TYPE_d6='Old Multiuser DOS secured FAT16 >=32M'
TYPE_d8='CP/M-86'
TYPE_da='Non-FS Data'
TYPE_db='Digital Research CP/M, Concurrent CP/M, Concurrent DOS'
TYPE_dd='Hidden CTOS Memdump'
TYPE_de='Dell PowerEdge Server utilities (FAT fs)'
TYPE_df='BootIt EMBRM'
TYPE_e0='Reserved by STMicroelectronics for a filesystem called ST AVFS.'
TYPE_e1='DOS access or SpeedStor 12-bit FAT extended partition'
TYPE_e3='DOS R/O or SpeedStor'
TYPE_e4='SpeedStor 16-bit FAT extended partition < 1024 cyl.'
TYPE_e5='Tandy MSDOS with logically sectored FAT'
TYPE_e6='Storage Dimensions SpeedStor'
TYPE_e8='LUKS'
TYPE_eb='BeOS BFS'
TYPE_ec='SkyOS SkyFS'
TYPE_ee='MBR is followed by an EFI header'
TYPE_ef='EFI file system'
TYPE_f0='Linux/PA-RISC boot loader'
TYPE_f1='Storage Dimensions SpeedStor'
TYPE_f2='DOS 3.3+ secondary partition'
TYPE_f4='SpeedStor large partition'
TYPE_f5='Prologue multi-volume partition'
TYPE_f6='Storage Dimensions SpeedStor'
TYPE_f9='pCache'
TYPE_fa='Bochs'
TYPE_fb='VMware File System partition'
TYPE_fc='VMware Swap partition'
TYPE_fd='Linux raid partition with autodetect using persistent superblock'
TYPE_fe='LANstep'
TYPE_ff='Xenix Bad Block Table'

################################################################################

if [ -n "${HDD}" ]; then
	if [ -b ${HDD} -o -f ${HDD} ]; then
		exec < "${HDD}"
	else
		echo "${CMD_NAME}: \"${HDD}\" does not exit, or is not block-device or file" 1>&2
		exit 1
	fi
fi

if [ -z "${PARTITION}" ]; then
	START=0
	END=3
else
	if [ ${PARTITION} -lt 1 -o ${PARTITION} -gt 4 ]; then
		echo "${CMD_NAME}: partition number should be 1..4." 1>&2
		exit 1
	fi
	START=`expr ${PARTITION} - 1`
	END=${START}
fi

PART=`dd bs=1 skip=446 count=66 2> /dev/null | 	\
	hexdump -v -e	"			\
		 1/1 \"%02x\" \" \"	\
		 3/1 \"%3u \" \" \"	\
		 1/1 \"%02x\" \" \"	\
		 3/1 \"%3u \" \" \"	\
		 2/4 \"%10u \" \" \" "	\
	`

if [ "${PART:208:6}" != "55 170" ]; then
	echo "${CMD_NAME}: partition table may be corrupted."
fi

for ((p=${START} ; p <= ${END} ; p++ )) do
	FLAG=${PART:`expr ${p} \* 52 + 0`:2}
	HD=${PART:`expr ${p} \* 52 + 3`:3}
	SC=${PART:`expr ${p} \* 52 + 7`:3}
	CY=${PART:`expr ${p} \* 52 + 11`:3}
	S_HD=`expr ${HD}`
	S_SC=`expr ${SC} % 64`
	S_CY=`expr ${SC} / 64 \* 256 + ${CY}`
	TYPE=${PART:`expr ${p} \* 52 + 15`:2}
	eval "TYPE_INFO=\${TYPE_${TYPE}}"
	HD=${PART:`expr ${p} \* 52 + 18`:3}
	SC=${PART:`expr ${p} \* 52 + 22`:3}
	CY=${PART:`expr ${p} \* 52 + 26`:3}
	E_HD=`expr ${HD}`
	E_SC=`expr ${SC} % 64`
	E_CY=`expr ${SC} / 64 \* 256 + ${CY}`
	S_LBA=${PART:`expr ${p} \* 52 + 30`:10}; S_LBA=`expr ${S_LBA}`
	N_LBA=${PART:`expr ${p} \* 52 + 41`:10}; N_LBA=`expr ${N_LBA}`

	printf "Partition %d\n" "`expr ${p} + 1`"
	printf "\tBoot Flag\t= [%s]\n" "${FLAG}"
	printf "\tStart Hd/Sc/Cy\t= [%02x/%02x/%03x] %d/%d/%d\n" "${S_HD}" "${S_SC}" "${S_CY}" "${S_HD}" "${S_SC}" "${S_CY}"
	printf "\tPartition Type\t= [%s] \"%s\"\n" "${TYPE}" "${TYPE_INFO}"
	printf "\tEnd Hd/Sc/Cy\t= [%02x/%02x/%03x] %d/%d/%d\n" "${E_HD}" "${E_SC}" "${E_CY}" "${E_HD}" "${E_SC}" "${E_CY}"
	printf "\tStart LBA\t= [%08x] %'d sector (%'d bytes offset)\n" "${S_LBA}" "${S_LBA}" `expr "${S_LBA}" \* 512`
	printf "\tNumber of LBA\t= [%08x] %'d sectors (%'d bytes)\n" "${N_LBA}" "${N_LBA}" `expr "${N_LBA}" \* 512`
done