仙石浩明の日記

2023年7月5日

exFAT な Ubuntu ブータブル USBメモリ (exFAT Bootable USB Flash Drive) の作り方

USBメモリや DVD などのリムーバブルメディアから起動可能な Ubuntu (以下 「Live Ubuntu」 と呼ぶ) は、 PC がトラブったときのレスキュー (障害復旧) の道具として重宝する。 最近の USBメモリは容量が大きく、 3GB 程度の Live Ubuntu を入れておいても大して邪魔にならない。 ふだん持ち歩く USBメモリにも、 それぞれ Live Ubuntu を入れておくとイザというとき便利。

ところが、 Ubuntu (公式) が公開している Live Ubuntu は exFAT からの起動に対応していない。 exFAT は FAT の後継として Microsoft が開発したファイルシステムで、 従来 4GB までのファイルしか扱うことができなかった FAT の制約が大幅に緩和されている。

昨今の動画ファイルはサイズが 4GB を超えるものも多く、 USBメモリは FAT ではなく exFAT でフォーマットしたい。 もちろん、 NTFS でフォーマットすれば大きなファイルを入れられるし、 Live Ubuntu も起動できるが、 NTFS は USBメモリには牛刀すぎる。

USBメモリは様々な機器に挿す可能性があるわけで、 NTFS にするのは躊躇してしまう。 スマホやラズパイ、 さらにはコンビニ等のプリント機など、 その全てで NTFS が問題無く使えるのだろうか? やっぱりデフォルトである exFAT のほうが安心。

Ubuntu などのインストールメディアの ISO イメージを exFAT に置いて起動する方法 (Make an exFAT Bootable USB Flash Drive) が既に公開されているが、 仕組み (Ventoy) が複雑だし、 そもそも ISO イメージを loopback デバイス経由でマウントして起動すると重くなるし、 必要なメモリ量も多いので、 利用したいとは思わなかった。 トラブった PC のスペックが低い場合など、 道具は軽ければ軽いほど好ましい。

というわけで、 exFAT でフォーマットした USBメモリに、 Live Ubuntu を入れる方法を考えてみた。 要は Live Ubuntu の initrd ファイル (cpio アーカイブ) をどう改変して exFAT に対応させるか?

基本的には Live Ubuntu の initrd を展開して exFAT に対応させる修正を行なった後、 cpio コマンドを使って initrd を作り直せばいいのだが、 initrd は複数の cpio アーカイブをつなげた形になっていて、 作り方によっては互換性の問題が起きるかも? Live Ubuntu 自身の update-initramfs コマンドを使って initrd を更新したほうが手軽だし確実。

なお、 (PC の起動に必要な) UEFI パーティションは (いまのところ) FAT でフォーマットしておいた方が無難と思われる。 ほとんど (全て?) の PC が exFAT や NTFS でフォーマットした UEFI パーティションを認識するらしいが、 最大限の互換性を求めるなら FAT にしておくべきだろう。

というわけで、 USBメモリの末尾 4MB (以下の実行例では sdc2) だけ FAT でフォーマットして UEFI パーティションとし、 残り (sdc1, ここでは 「boot パーティション」と呼ぶ) を exFAT でフォーマットする:

senri:/ # gdisk -l /dev/sdc
GPT fdisk (gdisk) version 0.8.4

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Disk /dev/sdc: 121098240 sectors, 57.7 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 619FAAB5-5325-4404-99C4-F0541D53B069
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 121098206
Partitions will be aligned on 2-sector boundaries
Total free space is 0 sectors (0 bytes)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048       121090047   57.7 GiB    0700  Microsoft basic data
   2       121090048       121098206   4.0 MiB     EF00  EFI system partition
   3              34            2047   1007.0 KiB  EF02  BIOS boot partition
senri:/ # mkfs -t fat /dev/sdc2
mkfs.fat 4.0 (2016-05-06)
senri:/ # mkfs -t exfat /dev/sdc1
mkexfatfs 1.2.3
Creating... done.
Flushing... done.
File system created successfully.
senri:/ # 

UEFI パーティションは Windows ユーザ (あるいはコンビニのプリント機) からは見えないので、 boot パーティションのみを、 ふつうの USBメモリとして使うことになる。

次に grub-install コマンドで UEFI パーティションに GRUB (ブートローダ) をインストールする。 ついでに UEFI に対応していない PC (は滅多にないと思うが) でも起動できるように、 「--target i386-pc」オプションを使って MBR にも GRUB をインストールしておく。

senri:/ # mount /dev/sdc1 /mnt/usb
senri:/ # mount /dev/sdc2 /mnt/efi
senri:/ # grub-install --target x86_64-efi --efi-directory /mnt/efi --boot-directory=/mnt/usb/boot --removable
Installing for x86_64-efi platform.
Installation finished. No error reported.
senri:/ # grub-install --target i386-pc --boot-directory=/mnt/usb/boot --removable /dev/sdc
Installing for i386-pc platform.
Installation finished. No error reported.
senri:/ # blkid | grep sdc1
/dev/sdc1: UUID="E7F3-77CD" BLOCK_SIZE="512" TYPE="exfat" PTTYPE="dos" PARTLABEL="Microsoft basic data" PARTUUID="23041859-cc53-4164-be6a-44af6a966e5d"
senri:/ # 

UEFI パーティションに書込まれるのは GRUB コア (/EFI/BOOT/BOOTX64.EFI) のみで 124KB しかない。 UEFI パーティションは 4MB も要らないかも? GRUB 本体 (/boot/grub/x86_64-efi ディレクトリ) は boot パーティションに書込まれる。 /EFI/BOOT/BOOTX64.EFI は /boot/grub/x86_64-efi/core.efi の内容と同じ。

GRUB の設定ファイル /boot/grub/grub.cfg は ↓ こんな感じ:

set uuid="E7F3-77CD"
insmod all_video

menuentry "ubuntu 22.04.2 desktop amd64" {
  linux /casper/vmlinuz boot=casper uuid=$uuid
  initrd /casper/initrd
}

ここで "E7F3-77CD" は USBメモリの exFAT パーティション /dev/sdc1 の UUID (Universally Unique Identifier, 汎用一意識別子)。 blkid コマンドなどで調べることができる (上記 grub-install の実行例の末尾を参照)。

/casper ディレクトリは、 Ubuntu DVD (以下の実行例では /cdrom にマウントしている) からコピーする:

senri:/ # cp -a /cdrom/casper /mnt/usb/
senri:/ # ls -la /mnt/usb/casper
total 2813952
drwxr-xr-x 2 root root      32768 Feb 23 13:13 .
drwxr-xr-x 4 root root      32768 Jul  3 18:10 ..
-rwxr-xr-x 1 root root      59931 Feb 23 13:09 filesystem.manifest
-rwxr-xr-x 1 root root       2885 Feb 23 13:09 filesystem.manifest-minimal-remove
-rwxr-xr-x 1 root root       3578 Feb 23 13:09 filesystem.manifest-remove
-rwxr-xr-x 1 root root         11 Feb 23 13:09 filesystem.size
-rwxr-xr-x 1 root root 2731876352 Feb 23 13:09 filesystem.squashfs
-rwxr-xr-x 1 root root        833 Feb 23 13:12 filesystem.squashfs.gpg
-rwxr-xr-x 1 root root  137120699 Feb 23 13:09 initrd
-rwxr-xr-x 1 root root   12186376 Feb 23 13:09 vmlinuz
senri:/ # 

filesystem.squashfs が Live Ubuntu の root ファイルシステム。 これ以外の filesystem.* は不要なので削除して構わない。 vmlinuz が Linux カーネルで、 initrd がこれから書き換える initrd ファイル。

boot パーティションが FAT 等であれば、 この状態でブータブル USBメモリとして機能するが、 exFAT だと initrd が /casper/filesystem.squashfs を見つけられず Ubuntu を起動できない。

そこで initrd の内容を書き換えて exFAT を扱えるようにする。 といっても Linux Kernel 5.4 以降は (以前の exfat-fuse ではなく) カーネルレベルで exFAT を扱うことができる。 つまり必要なのはカーネルモジュール kernel/fs/exfat/exfat.ko を initrd へ追加することだけ。

まず Ubuntu DVD を用いて Live Ubuntu を起動する (インストール DVD から起動して「Ubuntu を試す」を選択)。 /etc/initramfs-tools/modules および /usr/share/initramfs-tools/scripts/casper-helpers に以下のパッチ ubuntu.patch をあてる:

--- etc/initramfs-tools/modules~	2023-02-23 12:59:33.000000000 +0900
+++ etc/initramfs-tools/modules	2023-07-03 08:46:22.000000000 +0900
@@ -9,3 +9,4 @@
 #
 # raid1
 # sd_mod
+exfat
--- usr/share/initramfs-tools/scripts/casper-helpers~	2022-05-30 23:40:38.000000000 +0900
+++ usr/share/initramfs-tools/scripts/casper-helpers	2023-07-03 08:48:03.000000000 +0900
@@ -36,7 +36,7 @@
     # FIXME: do something better like the scan of supported filesystems
     fstype="${1}"
     case ${fstype} in
-        vfat|iso9660|udf|ext2|ext3|ext4|btrfs|ntfs)
+        vfat|exfat|iso9660|udf|ext2|ext3|ext4|btrfs|ntfs)
             return 0
             ;;
     esac
@@ -234,7 +234,7 @@
             # will cause data loss when a live CD is booted on a system
             # where filesystems are in use by hibernated operating systems.
             case "$(get_fstype ${devname})" in
-                vfat)
+                vfat|exfat)
                     :;;
                 *)
                     continue;;
@@ -337,7 +337,7 @@
         for dev in $(subdevices "${sysblock}"); do
             devname=$(sys2dev "${dev}")
             case "$(get_fstype ${devname})" in
-                vfat|ext2)
+                vfat|exfat|ext2)
                     :;;
                 *)
                     continue;;
@@ -367,7 +367,7 @@
 is_supported_fs(){
     [ -z "${1}" ] && return 1
     case ${1} in
-        ext2|ext3|ext4|xfs|jfs|reiserfs|vfat|ntfs|iso9660|btrfs)
+        ext2|ext3|ext4|xfs|jfs|reiserfs|vfat|exfat|ntfs|iso9660|btrfs)
             return 0
             ;;
     esac
@@ -388,6 +388,7 @@
     modprobe xfs
     modprobe jfs
     modprobe vfat
+    modprobe exfat
     modprobe fuse
     [ "$quiet" != "y" ] && log_end_msg "...devs loaded..."
     touch /dev/.initramfs/lupin-waited-for-devs

この ↑ パッチでは、 起動時に (つまり initramfs で) 必要なカーネルモジュールを指定するファイル /etc/initramfs-tools/modules に 「exfat」 を追記している。 また、 initramfs 内のスクリプト 「/usr/share/initramfs-tools/scripts/casper-helpers」 を boot パーティションが exFAT でもエラーにならないよう修正している。

というか、 たったこれだけの修正で exFAT から起動できるのだから、 公式の Live Ubuntu で exFAT からの起動をサポートして欲しい。 exFAT を除外する理由でもあるのだろうか?

そして update-initramfs.distrib コマンドを使って新しい initrd を生成する。 ここで (通常の Ubuntu と同じ感覚で) update-initramfs を使ってしまうと 「update-initramfs is disabled since running on read-only media」 と言われてしまうので注意。 「read-only media」 だからダメなのではなく、 Live Ubuntu の update-initramfs は、 このメッセージを出力するだけの sh スクリプトに置き換えられている (いったい何のために?)。

例えば Live Ubuntu の Terminal を使って以下のように実行する:

root@ubuntu:/# wget https://www.gcd.org/sengoku/docs/ubuntu-22.04.2-desktop-amd64.patch
--2023-07-04 01:47:50--  https://www.gcd.org/sengoku/docs/ubuntu-22.04.2-desktop-amd64.patch
Resolving www.gcd.org (www.gcd.org)... 71.19.146.203, 74.207.241.21, 219.94.252.139, ...
Connecting to www.gcd.org (www.gcd.org)|71.19.146.203|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1799 (1.8K) [text/plain]
Saving to: ‘ubuntu-22.04.2-desktop-amd64.patch’

2023-07-04 01:47:57 (38.1 MB/s) - ‘ubuntu-22.04.2-desktop-amd64.patch’ saved [1799/1799]

root@ubuntu:/# patch -p0 < ubuntu-22.04.2-desktop-amd64.patch
patching file etc/initramfs-tools/modules
patching file usr/share/initramfs-tools/scripts/casper-helpers
root@ubuntu:/# uname -a
Linux ubuntu 5.19.0-32-generic #33~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Mon Jan 30 17:03:34 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
root@ubuntu:/# update-initramfs.distrib -c -k 5.19.0-32-generic
update-initramfs: Generating /boot/initrd.img-5.19.0-32-generic
cryptsetup: ERROR: Couldn't resolve device /cow
cryptsetup: WARNING: Couldn't determine root device
W: Couldn't identify type of root file system for fsck hook
root@ubuntu:/# 

生成された /boot/initrd.img-5.19.0-32-generic を USBメモリの boot パーティションの /casper/initrd へコピーする。

以上で、 Ubuntu が起動できる exFAT な USBメモリができた。 ふだんは普通の USBメモリとして使える。 /casper/filesystem.squashfs は 2605MB もあるが、 いつでも Ubuntu DVD からコピーすることで元に戻せるので、 USBメモリに空きがないときは気軽に削除して構わない。

Filed under: システム構築・運用 — hiroaki_sengoku @ 07:55

No Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment