usbfadmは、河豚板でUSBメモリを管理するためのユティリティツールであり、河豚板の機能の中核を担っている。
これは、USBメモリを扱うためのツールであるが、河豚板LiveUSBの登場以前から存在しており、当初は河豚板LiveCDで、mfsの内容をUSBメモリに保存し次回の起動で読み込む、河豚板LiveCDの補助ツールだった。
利用は、以下のように対話形式で行うのが基本。
nimbus33$ doas usbfadm doas (kaw@nimbus33.honjoji.local) password: Welcome to usbfadm. USB flash drive administration tool for FuguIta Version/Arch: 7.6/amd64 (FuguIta-7.6-amd64-202504091) Boot mode: usbflash Target device: /dev/sd2d Data saved as: nimbus33 readline capability available TAB to complete the reserved words Type ? for help. sd2d : nimbus33 ->? Interactive commands are; target - set the partition for sync, info and expand saveas - set the name of the data to be saved sync - sync the target with the current mfs archive - archive saved directory to *.cpio.gz info - show info about the target partition newdrive - make a new FuguIta LiveUSB expand - expand the target partition as large as possible bye, exit, quit - end of this utility Command line options are; -r : redo sync non-interactively (must run 'sync' at interactive mode before doing this) -i : show info about the persistent storage -q : quiet mode when redo sync -t : trace output (pass -x to shell) -d : debug output for newdrive to file 'usbf.debugout' -h : print this help sd2d : nimbus33 ->
メモリ上のファイルシステムの内容をUSBメモリへ保存(同期)する。
河豚板LiveUSBを新規作成(リマスタリング)する。
カスタマイズ項目 | 設定内容 | デフォルト値(amd64) |
生成対象 | 実デバイス / イメージファイル | なし |
起動方法 | Legacy BIOS / UEFI / なし(データ保存専用) / Hybrid | UEFI |
パーティション テーブル | MBR / GPT | MBR |
/ramの ファイルシステム | MFS / TMPFS | MFS |
スワップのサイズ | (0で作成しない) | 16MB |
データ保存領域の サイズ | (0で作成しない) | 未使用部分全て |
データ保存領域の 暗号化 | なし / あり | なし |
未使用領域を FATにするか | しない / する | しない |
なお、上の図からわかるように、newdriveでは保存データのコピーは行わないため、元のシステムと同じ環境が必要な場合は、別途、targetを新デバイスに変更した上でsyncを実行する必要がある。
デバイスに未使用領域がある場合、データ保存用パーティションをデバイスのサイズ一杯まで拡張する。
河豚板LiveUSBのイメージファイルは、現在、2GBのサイズで作成され、配布されている。
よって、それ以上のサイズのUSBメモリにイメージを書き込んで使用することになるが、書き込んだだけでは使用するUSBメモリのサイズに関係なく2GBしか領域を使用できない。そして、河豚板のシステムが約1GBを占有しているので、usbfadmでデータを保存できるのは1ギガバイト程度となる。
expandコマンドは、データ保存用パーティションをデバイスのサイズ一杯まで拡張し、デバイスを有効に使用できるようにする。
saveasで指定した保存データをアーカイブ化する。
saveasコマンドで指定し、syncコマンドで保存したファイルツリーから*.cpio.gz形式のアーカイブを作成する (パス名の最大長は、tarよりもcpioが長くとれることからcpioを選択した)。
このアーカイブファイルは、syncで保存したデータと同様、河豚板の起動モード3で指定して読み込むことができる。
test.cpio.gzのsaveasが再起動後、"test"に設定されていると... ----->archive---->sync...reboot...-->sync---> | | ^ | V V | V test.cpio.gz test/ | test/ "test"が、より古い | | test.cpio.gzで +------------------------+ 上書きされる危険あり!
usbfadmは、ディスクデバイスを管理するコマンドに対するwrapper scriptという見方もできる。
usbfadmが提供する機能は、fdisk, disklabel, newfs, bioctlなど、ディスクデバイスを扱うコマンドを直接使用することで実行できるが、usbfadmはそれらコマンドの詳細を知ることなく、河豚板LiveUSBの管理を安全かつ容易に実行可能。
usbfadmは、単一のkshスクリプトで、概ね以下のような構成になっている。
参考: usbfadmのソースコード (GitHub)
#ユティリティ関数群の定義 echoerr() { ... } clear_exit() { ...} ... #各コマンド関数群の定義 cmd_sync() { ... } cmd_archive() { ... } ... #メイン処理 #初期化 大域変数の定義 実行環境のチェック デフォルト値の設定 設定ファイルの読込 コマンドラインオプションの解析 if コマンドラインオプションあり; then 非対話的処理 (usbfadm -rなど) clear_exit fi #対話的処理 while :; do プロンプトの表示 コマンドの読込 case コマンド in sync) cmd_sync;; archive) cmd_archive;; ... esac done clear_exit
コマンドの読込には、rlwrapコマンドを利用したrl_wread関数を定義。この関数により、以下の機能が実装されている。
rl_wread関数は、始めにrlwrapコマンドをダミーで起動し、rlwrapコマンドが正常に使えるか判定する。rlwrapが正常に使用できない場合は、単純なread文にフォールバックする。
#------------------- # read user's input with readline functionality # outputs echoed to stdout # # usage: rl_wread prompt-str default-str [completion words ....] # rl_wread () { local prompt="$1"; shift local default="$1"; shift local retval # check if rlwrap is available # When control tty is missing (in /etc/rc.shutdown for example), # rlwrap in command substitution "$(rlwrap ...) " fails. if retval=$(rlwrap true) 2>/dev/null 2>&1 ; then echo "$@" > $lockdir/rl_words rlwrap -b '' \ -f $lockdir/rl_words \ -P "$default" \ sh -f -c 'echo -n "'"$prompt"'->" >&2 ; read w || echo EOF; echo $w' || echo RL_ERR else #------------------- # fallback to dumb input # if [[ -z "$default" ]]; then echo -n "${prompt}->" >&2 read w else echo -n "$prompt [$default] -> " >&2 read w if [[ -z "$w" ]]; then w="$default" fi fi echo $w fi }
使用法: ユーザの入力値は標準出力に返されるので、シェルのコマンド置換でキャプチャする。
end of fileの場合は文字列「EOF」が、エラーが発生した場合は「RL_ERR」が返される。
cmd=$(rl_wread "$d : $u " '' quit bye exit sync archive info saveas target newdrive expand help ?) ↑ ↑ ↑ プロンプト ↑ TABで補完する単語群 デフォルト値
usbfadmは、OSによって抽象化されたデバイスやファイルを扱うので、処理の大部分はプラットフォーム非依存だが、newdrive機能では、起動廻りの処理を扱うのでプラットフォームに依存する部分が存在する。
この処理は、newdrive()関数内で/etc/fuguita/usbfadm_postproc.shを読み込む(sourceする)ことで対応している。
例: (ビルドシステムの) lib/usbfadm_postproc.sh.arm64
河豚板/arm64では、newdrive時にラズパイ3/4用にEFI/U-boot関連の設定が必要なので、その処理をusbfadm_postproc.sh.arm64で行っている。
# # post processing for Raspberry Pi 3/4 # This file is included in usbfadm # notice "Change partition ID and Boot flag for Raspberry Pi..." if [ "$instsys" = UEFI ]; then echo "e 0\n0C\n\n\n\nf 0\nq" | fdisk -e "$scandev" ← パーティションタイプの変更 fi # install Raspberry Pi Firmwares and U-Boot binaries # notice "Copying U-BOOT stuffs..." if mount -t msdos -o-l /dev/${scandev}i /mnt; then tar -xvz -C /mnt -f /usr/fuguita/mdec/bootstuff.$(uname -m).tar.gz ← ESPにブート関連の umount /mnt ファイルを配置 fi
expand機能は、従来、MBRパーティションにのみ対応していた。河豚板LiveUSBの配布イメージはMBR(+UEFI)で行っており、これで必要十分であったが、GPTの場合にもデータ保存領域の拡張が行えるようexpand機能の改良を行った。
usbfadmではパーティションの操作を行う際は、行いたい操作に操作するfdiskやdisklabelのコマンド文字列を生成し、これをパイプライン経由でそれらのコマンドに入力することで実現している。
コーディング例
fdisk_input="e ${partid_obsd}\nA6\nn\n\n*\nw\nq\n" ... echo -n "$fdisk_input" | fdisk -e "$scandev" >/dev/null
GPTのパーティション拡張の場合も、概ねこの方針でOKだが、新たに以下の問題が発生する。
GPTの再構成方法の概要は以下のとおり
# GPTが存在し、PrimaryとSecondaryが同じことを確認 Primary GPT: Disk: sd1 Usable LBA: 34 to 16777182 [16777216 Sectors] GUID: c535a72e-969c-4602-b77c-e7a162b549b0 #: type [ start: size ] guid name ------------------------------------------------------------------------ 1: EFI Sys [ 64: 1024 ] ←パーティション1の種別、位置、サイズ 3df0b410-40b7-47be-a406-20da01ece8e4 UEFI Boot ←パーティション1のUUID、コメント 2: Microsoft basic data [ 12860480: 3916672 ] 以下同じ 8b830bfc-2d7f-4f00-9c1f-6c241aba9421 MSDOS FAT 3: OpenBSD [ 1088: 12859392 ] 83b8e6d4-e620-47aa-b1fc-04f7f827fa81 OpenBSD Area Secondary GPT: Disk: sd1 Usable LBA: 34 to 16777182 [16777216 Sectors] GUID: c535a72e-969c-4602-b77c-e7a162b549b0 #: type [ start: size ] guid name ------------------------------------------------------------------------ 1: EFI Sys [ 64: 1024 ] 3df0b410-40b7-47be-a406-20da01ece8e4 UEFI Boot 2: Microsoft basic data [ 12860480: 3916672 ] 8b830bfc-2d7f-4f00-9c1f-6c241aba9421 MSDOS FAT 3: OpenBSD [ 1088: 12859392 ] 83b8e6d4-e620-47aa-b1fc-04f7f827fa81 OpenBSD Area MBR: Disk: sd1 geometry: 1044/255/63 [16777216 Sectors] Offset: 0 Signature: 0xAA55 Starting Ending LBA Info: #: id C H S - C H S [ start: size ] ------------------------------------------------------------------------------- 0: EE 0 0 2 - 1044 85 1 [ 1: 16777215 ] EFI GPT ← MBRのパーティション0が、protective 1: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused MBRになっていることを確認 2: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused 3: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unusedusbfadmは、この出力を読み込んで、以下のようなfdisk編集モードのコマンド列に変換する。
reinit gpt ← GPTで初期化 edit 0 ← 初期化で作成されたパーティションを削除 0 edit 1 ← パーティション1の作成 c12a7328-f81f-11d2-ba4b-00a0c93ec93b ← UUID (パーティション種別) 64 ← 開始位置 1024 ← サイズ UEFI Boot ← コメント edit 2 ← パーティション2以下、同様 ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 12860480 3916672 MSDOS FAT edit 3 824cc7a0-36a8-11e3-890a-952519ad3f61 1088 12859392 OpenBSD Area write quitこれをfdisk -e sd1に入力し、GPTを修正する。
なお、この処理はGPTを全面的に書き換えるクリティカルな処理のため、以下のようなエラー対策を実施している。
GPT拡張後、disklabelコマンドを呼び出してデータ保存用パーティションの拡張を行う。この部分は従来のMBRパーティションの場合と同じ。
if [ 1 -le $(expr X"$(pwd)" : X$mntdir1) ]; then echo echoerr 'You are under $mntdir1. Please move to other directory.' exit 1 fiこのような古い形式のコードは、適時、修正してゆく。ただし、拙速な修正はエンバグを招きかねないため、今後の修正・開発作業に併せて、このようなコードのリファクタリングを行ってゆく予定