前回カーネルも差し替えたので、以前の記事を見ながらディスクイメージのバックアップを取ろうかと思ったのですが、以前に構築した Linux Mint の仮想環境で、VirtualBox が(なぜか)ホスト側に挿した microSD カードリーダをうまく認識してくれませんでした。
仕方がないので、ディスクイメージをコネコネして、パーティションのリサイズやら何やらを行なったディスクイメージを作成することにしました。
gparted でリサイズしようと思ったんですが、アレ?イメージファイルだと、何故かリサイズ出来ないですね。。。

ちなみに、前回カーネルを差し替えたものに加えて、
NTP設定不要デーモンの停止、RAMディスクの使用、SWAPの停止、を行なっています。
SD使用量の節約のため日本語化等は見送りました。
ディスク使用量は半分弱といったところです。
$ df
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/root        3671884 1623100   1849424  47% /
devtmpfs          219996       0    219996   0% /dev
tmpfs              44836     336     44500   1% /run
tmpfs               5120       0      5120   0% /run/lock
tmpfs              89660       0     89660   0% /run/shm
/dev/mmcblk0p1     57288   10200     47088  18% /boot
tmpfs              32768       0     32768   0% /tmp
tmpfs              32768     112     32656   1% /var/log

◆ディスクイメージのベースの作成
"Win32 Disk Imager" を使用して、microSD のディスクイメージを Read し、仮想環境からも見える共有フォルダに入れておきます。以下、仮想環境の Linux Mint での操作になります。
(以下、ディスクイメージを rpi.img というファイル名で取得したとして記載しています。)
$ fdisk -l /media/sf_shared/rpi.img
ディスク /media/sf_shared/rpi.img: 3951 MB, 3951034368 バイト
ヘッド 255, セクタ 63, シリンダ 480, 合計 7716864 セクタ
Units = セクタ数 of 1 * 512 = 512 バイト
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O サイズ (最小 / 推奨): 512 バイト / 512 バイト
ディスク識別子: 0x0009bf4f

                デバイス ブート    始点      終点  ブロック   Id  システム
/media/sf_shared/rpi.img1          8192    122879    57344    c  W95 FAT32 (LBA)
/media/sf_shared/rpi.img2        122880   7716863  3796992   83  Linux

まずは、作成するディスクイメージのサイズを決めます。
今回は4GBのmicroSDをしようしているので、半分の2GBにしてみようと思います。

まず、空のファイルをイレモノとして作ります。
元のサイズの半分にした値
  (7716863 + 1) / 2 = 3858432
をcountに指定します。
$ export IMGFILE=/media/sf_shared/new.img

$ dd if=/dev/zero of=${IMGFILE} count=3858432 bs=512
これで、単にゼロ埋めされた2GBのファイルが出来上がりました。
# ちなみに、fdisk の表示される値と単位を合わせる意味で bs=512 としていますが、
# bs を(8倍の) 4096 とかにした方が、ファイルの作成時間は短くなります。
# その場合、count は(8分の1の)482304 とする必要があります。
# 詳細は↓おまけ1参照

パーティションテーブルを作成し、パーティションを2つ作成します。
fdisk コマンドで対話モードで設定しても全然問題ないんですが、表現が難しいので下記のようなコマンドにしておきます。(凄く見にくいですが、$'\n' が改行と思ってください。)
$ echo o$'\n'w | fdisk ${IMGFILE}
$ echo n$'\n'p$'\n'$'\n'8192$'\n'122879$'\n'w | fdisk ${IMGFILE}
$ echo n$'\n'p$'\n'$'\n'122880$'\n'3858431$'\n'w | fdisk ${IMGFILE}
$ echo t$'\n'1$'\n'c$'\n'w | fdisk ${IMGFILE}

これでガワだけは出来ました。
$ fdisk -l ${IMGFILE}

ディスク /media/sf_shared/new.img: 1975 MB, 1975517184 バイト
ヘッド 255, セクタ 63, シリンダ 240, 合計 3858432 セクタ
Units = セクタ数 of 1 * 512 = 512 バイト
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O サイズ (最小 / 推奨): 512 バイト / 512 バイト
ディスク識別子: 0x0177f305

                デバイス ブート    始点      終点  ブロック   Id  システム
/media/sf_shared/new.img1          8192    122879    57344    c  W95 FAT32 (LBA)
/media/sf_shared/new.img2        122880   3858431  1867776   83  Linux

# 最初はこのディスクイメージを "sudo kpartx -v -a ${IMGFILE}" して、
# "mkfs.ext4 /dev/mapper/loop0p2" とかして直接フォーマットしようとしたんですが、
# 何かうまく出来なくてmountも出来ないので、下記のような手順を踏む事にしました。

◆各パーティションイメージの作成
それぞれ
  (122879 + 1) - 8192 = 114688
  (3858431 + 1) - 122880 = 3735552
の値を指定し、各パーティションのイメージファイルを作成します。
$ dd if=/dev/zero of=boot.img count=114688 bs=512
$ dd if=/dev/zero of=rootfs.img count=3735552 bs=512
それぞれフォーマットします。
$ mkfs.vfat boot.img
$ mkfs.ext4 -F rootfs.img

元のイメージディスクをマウントします。
$ mkdir old1/ old2/ new1/ new2/
$ sudo kpartx -v -a /media/sf_shared/rpi.img
$ sudo mount /dev/mapper/loop0p1 old1/
$ sudo mount /dev/mapper/loop0p2 old2/
元のイメージディスクの中身をそれぞれ全コピーします。
$ sudo mount boot.img new1/
$ sudo mount rootfs.img new2/
$ sudo cp -a old1/* new1/
$ sudo cp -a old2/* new2/
その他変更等(.bash_history や /var/cache/* の削除等)、やりたければやって、気が済めばアンマウントします。
$ sudo umount new1/
$ sudo umount new2/
元のイメージディスクをアンマウントします。
$ sudo umount old1/
$ sudo umount old2/
$ sudo kpartx -d /media/sf_shared/rpi.img
$ rmdir old1/ old2/ new1/ new2/

◆ディスクイメージとして結合
ベースのディスクイメージの各パーティションを置き換えて出来上がりです。
$ dd if=boot.img of=${IMGFILE} seek=8192 bs=512
$ cat rootfs.img >> ${IMGFILE}
これを "Win32 Disk Imager" で microSD に書き込めば、いつでもこの環境に復帰できます。
ちなみに、このディスクイメージは圧縮したら 400MB 弱になりました。

◆おまけ1

dd コマンドでは "bs=512, count=2048" でも、"bs=1024, count =1024" でも、どちらでも 1MB (bs * count) の読み書きを行なう、という事には変わりありません。では動きとしてはどう違うのでしょうか?

dd コマンドでは、if で指定したファイルから bs 単位で read し、of で指定したファイルに bs 単位で write を行う、という操作を count 回数分繰り返します。
つまり bs 単位の read, write システムコールを count 回発行しているのです。

以下のように、strace で観察するとよく分かります。
$ strace dd if=/dev/zero of=dummy count=100 bs=512
execve("/bin/dd", ["dd", "if=/dev/zero", "of=dummy", "count=100", "bs=512"], [/* 48 vars */]) = 0
brk(0)                                  = 0x9cf4000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
            :
open("/dev/zero", O_RDONLY|O_LARGEFILE) = 3
dup2(3, 0)                              = 0
close(3)                                = 0
_llseek(0, 0, [0], SEEK_CUR)            = 0
open("dummy", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
dup2(3, 1)                              = 1
close(3)                                = 0
clock_gettime(CLOCK_MONOTONIC, {238, 286988438}) = 0
read(0, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 512) = 512
write(1, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 512) = 512
read(0, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 512) = 512
write(1, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 512) = 512
read(0, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 512) = 512
write(1, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 512) = 512

            :