KVM のディスクイメージのサイズを後から変更する

毎回忘れるのでメモ.

KVM のディスクイメージを後から変更してパーティションのサイズも変更する方法.
OpenWRT の KVM 用イメージを使ってるとディスクサイズが 50MB とか涙が出そうなサイズなので,こいつを 1 GB まで大きくします.

イメージは qcow2 でもできるけど,今回は raw で
まず,イメージファイルのサイズを変更します.
これは qemu-img コマンドで

qemu-img resize openwrt-x86-generic-combined-ext4.img 1G

これで,ディスクイメージのサイズは 1G になります.
が,VM から見たパーティションのサイズは変更前のままです.
なので,後からパーティションを追加して増やすことはできますが,そもそも root パーティションが少ないので,パーティションのサイズを変更したい場合に使えません.

なので,root パーティションを拡張します.
あ、ちなみに ext4 でしか確認してないです.
まずイメージファイルを /dev/loop0 に接続します.

losetup /dev/loop0 openwrt-x86-generic-combined-ext4.img

単一のパーティションならこんなことしなくても問題ないはずなんですが,VM のイメージファイルになるとブートローダとか,複数のパーティションが1つのイメージファイルに入っているので,まず /dev/loop0 に接続します.
で,次に loop0 の各パーティションは以下のコマンドで確認できます.

# kpartx -l /dev/loop0
loop0p1 : 0 9009 /dev/loop0 63
loop0p2 : 0 98721 /dev/loop0 9135

今回は loop0p2 のパーティションを後ろ目一杯まで大きくします.
というか loop0p1 は大きくしようとすると loop0p2 を後ろにズラさないとできません。。。

ってことで,以下の手順でサイズ変更

# fdisk /dev/loop0

Command (m for help): p

Disk /dev/loop0: 55 MB, 55008768 bytes
16 heads, 63 sectors/track, 106 cylinders, total 107439 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000

Device Boot Start End Blocks Id System
/dev/loop0p1 * 63 9071 4504+ 83 Linux
/dev/loop0p2 9135 107855 49360+ 83 Linux

Command (m for help): d
Partition number (1-4): 2

Command (m for help): n
Partition type:
p primary (1 primary, 0 extended, 3 free)
e extended
Select (default p): p
Partition number (1-4, default 2): 2
First sector (9072-107438, default 9072): 9135
Last sector, +sectors or +size{K,M,G} (9135-107438, default 107438):
Using default value 107438

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: Re-reading the partition table failed with error 22: Invalid argument.
The kernel still uses the old table. The new table will be used at
the next reboot or after you run partprobe(8) or kpartx(8)
Syncing disks.

やっていることは,
1. 今のパーティションテーブル情報を表示.この時 loop0p2 の start の値をコピーしておく.
2. loop0p2 を削除
3. 新しくパーティションを作成.First sector にコピーしておいた start の値を入力.Last sector は何も入れずにエンター
4. w コマンドを終わり
になります.
他のパーティションに影響がない範囲で,start sector が同じであればパーティション内部のデータは維持されます.が,start sector 間違えると壊れます.

ここまでで,パーティションテーブルの情報まで変更できたので,最後に resize2fs でパーティションを拡張します.
resize2fs は単一パーティションとなっているデバイス名を指定する必要があるので,以下の手順でデバイスが見えるようにします.

# kpartx -a /dev/loop0
# ls /dev/mapper
control loop0p1 loop0p2

ここで一度 e2fsck で整合性のチェック.
これをしておかないと後の resize2fs が実行できない場合があります.

# e2fsck -f /dev/mapper/loop0p2

最後に resize2fs で終わり.

resize2fs /dev/mapper/loop0p2

終わったら後片付け

# kpartx -d /dev/loop0
# losetup -d /dev/loop0

今回は kpartx を使いましたが,ディスクイメージを loopback マウントする時に offset で開始位置をズラせば実はいらなかったりします.
間違えると痛いので大体 kpartx 使いますが。。。