Raspbian SDカードイメージファイルの縮小
Jetson nano のSDカードをバックアップする に改訂版を公開しました。そちらを参照してください。
SDカードに作成した、RaspberryPiのオリジナルのカスタムブートディスク (Raspbian Busterのインストール、 Raspbian Buster Lite版のインストール参照) は、イメージファイルにバックアップしておくと、 再度カスタマイズ作業を行わなくても同じ環境を作成できる。
ただ、このイメージファイルはSDカードを丸ごとファイル化するので、元のSDカードより小さなSDカードにコピーできない。
(スペック上同じ容量のSDカードでも微妙にサイズが違ったりするので入らないことがある)
そこで、イメージファイルを縮小しておけば、小さなSDカードにもコピーできる(コピー時間も短縮できて一石二鳥)。
このとき、ディスクイメージ内部のパーティション情報/ファイルシステム情報をきちんと縮小処理しておかないと
ファイルシステムエラーになってしまうので、注意が必要。
今回はSDカードから作成したイメージファイルをUbuntuで縮小処理するスクリプトを書いてみた。
とりあえず、手元の環境では動いているが、詳細評価したわけではないので、
実行する場合は自己責任で。
また、途中エラーチェックは行っていないので、エラーが発生していないか、実行結果を注意深く確認すること。
SDカードのイメージファイルは大きいので、共有フォルダを使ってWindows側のフォルダをアクセスできるようにしておけばディスク領域を圧迫しなくて済む。
Virtualbox側で共有フォルダ「Share」を作成済みで、/Shareディレクトリにマウントする場合は以下のコマンドで。
sudo mount -t vboxsf Share /Share/
イメージファイル内のパーティションをそれぞれloopデバイスに割り当てるツールを使用する。
以下のコマンドでインストールできる。
sudo apt install kpartx
以下のスクリプトを適当な名前(例えばshrink_img.sh
)で保存し、実行する(bash shrink_img.sh
)。
実行する前に使用するファイル名を設定すること。
変数 | 内容 |
---|---|
WORK_IMG_FILE | 作業用イメージファイルのファイル名 |
NEW_IMG_FILE | 小さくしたイメージファイルのファイル名 |
WORK_IMG_FILE で指定したファイルは書き変えられるので、オリジナルのイメージファイルは 別途残しておくこと。
内部でsudo
を実行しているので、パスワードを聞かれたら入力する。
途中、『警告: パーティションを縮小するとデータを失うかもしれませんが、それでも実行しますか?』と聞かれたら「y」を入力する。
# イメージファイル
WORK_IMG_FILE=/Share/tmp.img # 作業ファイル
NEW_IMG_FILE=/Share/shrink.img # 縮小した(作成する)ファイル
# イメージファイルをマッピング(マッピング済みでも問題ない)
# 前回の操作が終わるのを待つため-sを付けておく
sudo kpartx -asv ${WORK_IMG_FILE}
# loopデバイス名を取得
# 前回の操作が終わるのを待つため-sを付けておく
tmp_var=(`sudo kpartx -ls ${WORK_IMG_FILE}`)
LOOP_DEV=/dev/mapper/${tmp_var[6]} # 結果の位置は決め打ちで(姑息だけど)
# 要求するブロックサイズの取得
tmp_var=(`sudo resize2fs -P ${LOOP_DEV}`)
req_fs_block=${tmp_var[-1]} # サイズは結果の最後に入っている
# パーティション情報の取得
part_info=`sudo parted -m ${WORK_IMG_FILE} unit B print`
# 1行毎に分割
line=(${part_info//;/ })
# ext4のパーティション番号を探す
part_number=0
part_start=0
target_type="ext4"
for l in ${line[@]} ; do
# 各要素に分割
elm=(${l//:/ })
elm_number=${elm[0]} # パーティション番号
elm_start=${elm[1]//B/} # ついでに最後のBを取り除いておく
elm_type=${elm[4]} # ファイルシステムタイプ
# echo $elm_number $elm_start $elm_type
if [ -n "${elm_type}" ] ; then
if [ ${elm_type} = ${target_type} ] ; then
# 対象のパーティションが見つかった
part_number=${elm_number}
part_start=${elm_start}
break
fi
fi
done
if [ ${part_number} = 0 ] ; then
echo ${target_type} "のパーティションが見つかりませんでした"
else
# 新しいパーティション終了位置を計算
new_fs_block=$((req_fs_block + 100000)) # 少し余裕(100000block=390MB)を持たせる
new_fs_byte=$((new_fs_block * 4096)) # byteに換算
part_end=$((part_start + new_fs_byte + 2048)) # さらに少し余裕を持たせる
img_size_mb=$(((part_end / (1024 * 1024)) + 10)) # MBに換算 ついでにしつこいくらいに余裕を持たせる
set -x #--------------------------------------
# ファイルシステムとパーティションの縮小
sudo e2fsck -f ${LOOP_DEV}
sudo resize2fs -p ${LOOP_DEV} ${new_fs_block}
parted ${WORK_IMG_FILE} unit B resizepart ${part_number} ${part_end}
# 縮小コピー
dd if=${WORK_IMG_FILE} of=${NEW_IMG_FILE} count=${img_size_mb} bs=1M
set +x #--------------------------------------
fi
# マッピング解除
sudo kpartx -d ${WORK_IMG_FILE}
縮小したイメージファイルから作成したSDカードはSDカードの容量をすべて使用できるようになっていない。
そこで、コピーしたSDカードでブートした後、パーティションを拡張する必要がある。
色々手順がめんどっちいので、スクリプト作成してみた。
とりあえず、手元の環境では動いているが、詳細評価したわけではないので、
実行する場合は自己責任で。
以下のスクリプトを適当な名前(例えばexpand_partition.sh
)で保存し、実行する(bash expand_partition.sh
)
成功したらリブートすること。
# ターゲットのデバイス
target_device=/dev/mmcblk0
# パーティション情報の取得
part_info=`sudo parted -m ${target_device} unit s print free`
# 1行毎に分割
line=(${part_info//;/ })
# 最終行
# declare -i last_index=${#line[@]}-1
# last_line=${line[$last_index]}
last_line=${line[$((${#line[@]}-1))]}
# :で区切られた各要素に分割
elm=(${last_line//:/ })
# 変数名付け替え
# last_number=${elm[0]}
# last_start=${elm[1]}
last_end=${elm[2]}
# last_size=${elm[3]}
last_type=${elm[4]}
# 確認
# echo last_number= $last_number
# echo last_start= $last_start
# echo last_end= $last_end
# echo last_size= $last_size
# echo last_type= $last_type
# 最後のパーティションがfreeか確認
if [ ${last_type} = free ] ; then
# ext4のパーティション番号を探す
part_number=0
target_type="ext4"
for l in ${line[@]} ; do
# 各要素に分割
elm=(${l//:/ })
elm_number=${elm[0]}
elm_type=${elm[4]}
if [ -n "${elm_type}" ] ; then
# echo $elm_number $elm_type
if [ ${elm_type} = ${target_type} ] ; then
part_number=${elm_number}
break
fi
fi
done
if [ ${part_number} = 0 ] ; then
echo "ext4のパーティションが見つかりませんでした"
else
# リサイズの実行
set -x
sudo parted ${target_device} resizepart ${part_number} ${last_end}
sudo resize2fs ${target_device}p2
set +x
echo "リブートしてください"
fi
else
echo "最終パーティションがfreeではありません"
fi