Ubuntu

Ubuntu20.04 Live CDを作ってみる 基本編

Ubuntu

USBフラッシュメモリーを差し込むとUbuntuが起動し、ブラウザが動作するとっても軽い環境を作れないか、と思い立った。
以前、KNOPPIXを使わせてもらっていたことがあり、とても便利だったのをイメージしている。



広告


そもそも、UbuntuのインストールディスクはLive CDになっていて、アレを軽い感じに仕上げればいいのかなと考えたが、実際には手順を整理するのがやっと。
調べていくと、Live CDとLive USBというのがあるようで、定義を確認してみた。

区分説明
Live CD光ディスクからOSを起動させ、ファイルをRAMディスクに保管する。
Live USB設定をセーブしたり、パッケージをUSBデバイス上にインストールできる。

今回は、Live CDを作る。CDという名前ではあるがDVDにしか入らないサイズになった。

過去のUbuntuを使ったISOの作成もやってみている。練習として、とても良いものだった。

Live CDを作ってみる

最初は、記事のずっと下のほう live-helper でLiveを作成した。
見つけた記事が日本語だったのと、教えてくれていることをそのままに実行すればできそうだったので、感覚をつかむのにとても良かった。

Ubuntu 20.04ではlive-helperというパッケージがなく、live-buildに変わっていた。
探してみると、ここで詳細かつ確実な手順を教えてくれていることが分かった。
Github / How to create a custom Ubuntu live from scratch

今回、教えてくれている手順に、日本語対応やセキュアブートへの対応など、細々した改変を入れているので、その箇所には[改変]と記載している。

通常は、コマンド行の先頭を#と$で書き分けたりしているのだが、コピペするコマンドがとにかく多いので、

ベースとなる環境で、一般ユーザーとして入力するコマンド
chrootした環境で、rootとして入力するコマンド

と、文字色・背景色で書き分けておく。

ミラーサーバー

Live CDを作成するにあたっては、大量のパッケージをダウンロードしてくることになる。
気に入らなければやり直しもするだろうし、ダウンロード先のサーバーの状態によってはダウンロードにとても時間が掛かったりもする。

こうしたことを考慮すると、自分でミラーサーバーを立てて、ローカルで自由に使えるようにするのが良いのではなかろうか。

ということで、実際にミラーサーバーを立ててみたところ、とても快適な作成環境を作ることができた。
(仮想ゲスト2台=ミラーサーバー+作業環境 を同じホストの上で動かしたところ、もう爆速)
最初の全パッケージのダウンロードには6時間ぐらい掛かったけれど、そんなの寝ている間に終わってしまう。

立てたミラーサーバーは、mirror.hogeserver.hogeddns.jp と記載している。
これは、ローカルでは実際に名前解決ができて使えるミラーサーバーになっている。

作業環境の構築

過去にやったことが影響するのも嫌なので、まっさらな環境で作業をすることにした。
VMware Workstation 16 Player + Ubuntu 20.04 LTS Desktop

今回の手順を試すと、ISOイメージができあがった段階でハードディスクイメージのサイズが16.7GB程になった。多少余裕を持たせておいた方が無難。
また、ファイルの圧縮処理が入りCPUを使うので、コアは多めに割り当てても良いのではないかと思う。
メモリは4GB程度は必要。

仮想ホストは、UEFI+セキュアブートにしておいた。
CreateLive.vmx

firmware = "efi"
efi.legacyBoot.enabled = "FALSE"
uefi.secureBoot.enabled = "TRUE"

この状態でUbuntu 20.04のISOをセットしてインストール。

求められるままにアップデートをしてインストール完了。

ついでに、open-vm-tools-desktopがインストールされているか確認。なければ入れておく。
コマンドをコピペできるので、便利。

シャットダウン後、仮想マシン設定でフォルダの共有として VMware という名前のものを用意した。
後でISOファイルを簡単に取り出すため。

この段階でハードディスクイメージをバックアップした。
サイズは8.5GB程だった。

最初に一度やること

[改変]構築に必要なパッケージをインストールする。
syslinux-commonとisolinuxはISOLINUXで起動する場合に必要になるものだが、念のため。

sudo apt install binutils debootstrap squashfs-tools xorriso grub-pc-bin grub-efi-amd64-bin mtools syslinux-common isolinux

[改変]署名済みのブートローダーとGrubがインストールされていることを確認しておく。

dpkg -l grub-efi-amd64-signed shim-signed
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                  Version                  Architecture Description
+++-=====================-========================-============-===============================================================
ii  grub-efi-amd64-signed 1.167.2+2.04-1ubuntu44.2 amd64        GRand Unified Bootloader, version 2 (EFI-AMD64 version, signed)
ii  shim-signed           1.40.7+15.4-0ubuntu9     amd64        Secure Boot chain-loading bootloader (Microsoft-signed binary)

※セキュアブートしているシステムでのことなので、ちゃんとインストールされているはず。

Live CDの作成環境を作る

debootstrapは、DebianシステムをMIRRORからTARGETにインストールして起動できるようにするコマンドで、Ubuntuでも使える。
次の実行すると $HOME/live-ubuntu-from-scratch/chroot/var/cache/apt/archives/ にパッケージをダウンロードしてきて、展開・設定する。
ubuntu manpage / debootstrap

mkdir $HOME/live-ubuntu-from-scratch
sudo debootstrap --arch=amd64 --variant=minbase focal $HOME/live-ubuntu-from-scratch/chroot http://ja.archive.ubuntu.com/ubuntu/

あるいは…
mkdir $HOME/live-ubuntu-from-scratch
sudo debootstrap --arch=amd64 --variant=minbase focal $HOME/live-ubuntu-from-scratch/chroot http://ftp.riken.go.jp/Linux/ubuntu/

ミラーサーバーを立てたなら…
mkdir $HOME/live-ubuntu-from-scratch
sudo debootstrap --arch=amd64 --variant=minbase focal $HOME/live-ubuntu-from-scratch/chroot http://mirror.hogeserver.hogeddns.jp/ubuntu/
I: Retrieving InRelease 
I: Checking Release signature
…
I: Configuring apt...
I: Configuring libc-bin...
I: Base system installed successfully.

variantにはminbaseを指定しているが、以下から選べるようになっている。

設定値効果
minbase必須パッケージとaptのみを含む。
builddビルドに必要なパッケージをインストールする。
fakechrootroot権限なしでパッケージをインストールする。
一般ユーザーが、その権限のままでchrootされた環境にパッケージをインストールすることを示している模様。
無指定aptを含む必須・重要の全パッケージを含むインストール。

SUITEとしてfocal(20.04のコードネーム)を指定している。

ファイルシステムをマウント。

sudo mount --bind /dev $HOME/live-ubuntu-from-scratch/chroot/dev
sudo mount --bind /run $HOME/live-ubuntu-from-scratch/chroot/run

chrootする。

sudo chroot $HOME/live-ubuntu-from-scratch/chroot

追加でファイルシステムをマウントし、環境変数を設定する。

mount none -t proc /proc
mount none -t sysfs /sys
mount none -t devpts /dev/pts
export HOME=/root
export LC_ALL=C

chroot環境のホスト名を設定。

echo "ubuntu-fs-live" > /etc/hostname

[改変]aptのsource.listを作成。ubuntu Japanese Team / 日本国内のダウンロードサイトにあるサイトを利用させていただく。
ローカルに立てたミラーを使うこともできて、試行錯誤の段階ではそれもいいと思われるが、リリースの時には公式のものに戻しておくのが良さそうだ。

cat <<EOF > /etc/apt/sources.list
deb http://ja.archive.ubuntu.com/ubuntu/ focal main restricted universe multiverse
deb-src http://ja.archive.ubuntu.com/ubuntu/ focal main restricted universe multiverse
deb http://ja.archive.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse
deb-src http://ja.archive.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse
deb http://ja.archive.ubuntu.com/ubuntu/ focal-updates main restricted universe multiverse
deb-src http://ja.archive.ubuntu.com/ubuntu/ focal-updates main restricted universe multiverse
EOF

あるいは…
cat <<EOF > /etc/apt/sources.list
deb http://ftp.riken.go.jp/Linux/ubuntu/ focal main restricted universe multiverse
deb-src http://ftp.riken.go.jp/Linux/ubuntu/ focal main restricted universe multiverse
deb http://ftp.riken.go.jp/Linux/ubuntu/ focal-security main restricted universe multiverse
deb-src http://ftp.riken.go.jp/Linux/ubuntu/ focal-security main restricted universe multiverse
deb http://ftp.riken.go.jp/Linux/ubuntu/ focal-updates main restricted universe multiverse
deb-src http://ftp.riken.go.jp/Linux/ubuntu/ focal-updates main restricted universe multiverse
EOF

ミラーサーバーを立てたなら、リリースするまでの試行錯誤は…
cat <<EOF > /etc/apt/sources.list
deb http://mirror.hogeserver.hogeddns.jp/ubuntu/ focal main restricted universe multiverse
deb http://mirror.hogeserver.hogeddns.jp/ubuntu/ focal-security main restricted universe multiverse
deb http://mirror.hogeserver.hogeddns.jp/ubuntu/ focal-updates main restricted universe multiverse
EOF

等々。

updateする。

apt update

[改変]必要パッケージのインストール。libterm-readline-gnu-perl(Perlのデバッガで履歴機能を有効にするそうだ)は、必要とは言えないと思うので外した。
Ubuntuサーバー構築入門 / libterm-readline-gnu-perlのインストール – PerlのTerm::ReadLine::Gnuモジュール

apt install systemd-sysv

machine-idを構成。machine-idは常に一意であるべきで、同じIDを持つマシンが複数あると不具合を来す、とのこと。
DHCPホスト識別子、カーネルがマシンIDにちなんで名付けられたディレクトリに保管される…等々。
debian wiki / MachineId

dbus-uuidgen > /etc/machine-id
mkdir /var/lib/dbus
ln -fs /etc/machine-id /var/lib/dbus/machine-id

※ディレクトリがなかったので作成。2022/01/16追記

dpkgがパッケージをインストールする際に、いかなるパッケージがインストールされても、/sbin/initctlをインストールしないように細工。
/bin/trueは、呼び出されたら何もせずに0を戻すプログラム…なるほど、インストールするとサービスを起動するようなパッケージがまともに動かないようにしているということだな、きっと。
Debian クイックリファレンス / 3.5.1 dpkg-divert コマンド

dpkg-divert --local --rename --add /sbin/initctl
ln -s /bin/true /sbin/initctl

事前の準備が済んだので、パッケージを更新。

apt upgrade

Live CDの中身を編集

Liveシステムに必要なパッケージをインストールする。※結構時間が掛かる。

apt install \
sudo \
ubuntu-standard \
casper \
lupin-casper \
discover \
laptop-detect \
os-prober \
network-manager \
resolvconf \
net-tools \
wireless-tools \
wpagui \
locales \
grub-common \
grub-gfxpayload-lists \
grub-pc \
grub-pc-bin \
grub2-common

カーネルのインストール。どちらか一方を選択。

apt install --no-install-recommends linux-generic           ← カーネル5.4
apt install --no-install-recommends linux-generic-hwe-20.04 ← カーネル5.13

※新しい方が新しいハードウェアに対応できるので追記。2022/08/08

グラフィカルインストーラーをインストールする。※結構時間が掛かる。

apt install \
ubiquity \
ubiquity-casper \
ubiquity-frontend-gtk \
ubiquity-slideshow-ubuntu \
ubiquity-ubuntu-artwork

キーボードの選択画面が表示されたら
Japan → Japanese の順に選択。

コンソールセットアップが表示されたら
Guess optimal character set を選択。

ウィンドウマネージャーのインストール。※結構時間が掛かる。

apt install \
plymouth-theme-ubuntu-logo \
ubuntu-gnome-desktop \
ubuntu-gnome-wallpapers

キーボードはJapanese→Japaneseを選択。

[改変]よく使うアプリケーションのインストール。pingを追加。

apt install \
clamav-daemon \
terminator \
apt-transport-https \
curl \
vim \
nano \
less \
inetutils-ping

Visual Studio Codeのインストール。※インストールしたいならば。

curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
install -o root -g root -m 644 microsoft.gpg /etc/apt/trusted.gpg.d/
echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list
rm microsoft.gpg
apt update
apt install code

Google Chromeのインストール。※インストールしたいならば。

wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list
apt update
apt install google-chrome-stable

Java JDK 8のインストール。※インストールしたいならば。

apt install openjdk-8-jdk openjdk-8-jre

使わないアプリケーションの削除。※削除したいならば。

apt purge \
transmission-gtk \
transmission-common \
gnome-mahjongg \
gnome-mines \
gnome-sudoku \
aisleriot \
hitori

最終的に使わなくなったパッケージを削除。

apt autoremove

[改変]言語サポートをインストール。※結構時間が掛かる。
check-language-support -l ja で必要パッケージが分かるので、それをそのまま投入。
ask ubuntu / How can I install one language by command-line

apt install --no-install-recommends `check-language-support -l ja`

[改変]ロケールの設定。デフォルトロケールとして2つ目のja_JP.UTF-8を選択している。最初はこれでMOZCが選択できたようだったけれど、手順を色々と改変していく中で何かが変わってしまったらしく、単に日本語が選択される。

dpkg-reconfigure locales

ロケールとして
 en_US ISO-8859-1
 en_US.UTF-8 UTF-8
 ja_JP.UTF-8.UTF-8
を選択。

デフォルトロケールとして
 ja_JP.UTF-8(2つ目)
を選択。

リゾルバの設定。

dpkg-reconfigure resolvconf

YESを選択。

NetworkManagerの設定編集。

/etc/NetworkManager/NetworkManager.conf

[main]
rc-manager=resolvconf
plugins=ifupdown,keyfile
dns=dnsmasq

[ifupdown]
managed=false

[device]
wifi.scan-rand-mac-address=no

設定変更を反映。

dpkg-reconfigure network-manager

※NetPlanは入っていないな…。

編集環境の後始末

machine-idを削除。削除しておけば、Live起動したときに自動で生成される。

truncate -s 0 /etc/machine-id

dpkgインストール先の待避を解除。

rm /sbin/initctl
dpkg-divert --rename --remove /sbin/initctl

[改変]クリーンアップして、chroot環境を終了する。.bash_historyはexit時に書き込まれるようなので、chrootを抜けてから削除する。

apt clean
rm -rf /tmp/*
umount /proc
umount /sys
umount /dev/pts
export HISTSIZE=0
exit
sudo rm $HOME/live-ubuntu-from-scratch/chroot/root/.bash_history

マウントポイントの解除。

sudo umount $HOME/live-ubuntu-from-scratch/chroot/dev
sudo umount $HOME/live-ubuntu-from-scratch/chroot/run

Live CDに収録するファイルを集める

CDイメージのディレクトリimage、およびサブディレクトリを作り、カーネルをコピーする。

cd $HOME/live-ubuntu-from-scratch
mkdir -p image/{casper,isolinux,install}
sudo cp chroot/boot/vmlinuz-**-**-generic image/casper/vmlinuz
sudo cp chroot/boot/initrd.img-**-**-generic image/casper/initrd

memtest86(BIOS/UEFI)をコピーする。
wgetで取得しているのは、ブータブルUSB用のイメージで、Grubでループバックして起動している。

sudo cp chroot/boot/memtest86+.bin image/install/memtest86+

wget --progress=dot https://www.memtest86.com/downloads/memtest86-usb.zip -O image/install/memtest86-usb.zip
unzip -p image/install/memtest86-usb.zip memtest86-usb.img > image/install/memtest86
rm -f image/install/memtest86-usb.zip

マニフェスト(インストールされているパッケージとバージョンの一覧)を作成する。
Ubiquityによるインストールが完了したとき、不要なパッケージが削除されるよう、filesystem.manifest-desktopから不要な行を削除している。

cd $HOME/live-ubuntu-from-scratch
sudo chroot chroot dpkg-query -W --showformat='${Package} ${Version}\n' | sudo tee image/casper/filesystem.manifest
sudo cp -v image/casper/filesystem.manifest image/casper/filesystem.manifest-desktop
sudo sed -i '/ubiquity/d' image/casper/filesystem.manifest-desktop
sudo sed -i '/casper/d' image/casper/filesystem.manifest-desktop
sudo sed -i '/discover/d' image/casper/filesystem.manifest-desktop
sudo sed -i '/laptop-detect/d' image/casper/filesystem.manifest-desktop
sudo sed -i '/os-prober/d' image/casper/filesystem.manifest-desktop

chrootで作成した環境を圧縮し、squashfsファイルを作成する。
Liveはこのファイルが展開されて実行されるようだ。
作業にはかなりCPUを使うので、コアを多く与えておくと早くできそうだ。

cd $HOME/live-ubuntu-from-scratch
sudo mksquashfs chroot image/casper/filesystem.squashfs
printf $(sudo du -sx --block-size=1 chroot | cut -f1) > image/casper/filesystem.size

※ファイルが存在すると mksquashfs が失敗するので、2回目以降は実行前に削除しておく。

インストーラーによくあるREADMEファイルを作成。

cd $HOME/live-ubuntu-from-scratch
cat <<EOF > image/README.diskdefines
#define DISKNAME  Ubuntu from scratch
#define TYPE  binary
#define TYPEbinary  1
#define ARCH  amd64
#define ARCHamd64  1
#define DISKNUM  1
#define DISKNUM1  1
#define TOTALNUM  0
#define TOTALNUM0  1
EOF

ブートローダーを準備

UEFI

[改変]UEFI+セキュアブートができるように、署名されたブートローダーとGrubが使いたい。
ファイルをパーティションの如く取り扱う方法をこの手順に教えてもらったので、それを応用してみる。
32MBの空ファイルを作り、それをVFATでフォーマットしてマウントして、そこにGrubをインストールする。

cd $HOME/live-ubuntu-from-scratch
mkdir -p work/bi
cd work

dd if=/dev/zero of=bi.img bs=1M count=32
sudo mkfs.vfat bi.img
sudo mount bi.img bi/

sudo grub-install --target x86_64-efi --efi-directory bi/ --boot-directory=bi/boot/ --removable --uefi-secure-boot
Installing for x86_64-efi platform.
Installation finished. No error reported.

※インストールされるファイルはシステム上にあるけれど、この方法だとあまり考えずに収集できる。

[改変][オプション]Ubuntuは32ビットのブートローダーを提供していない(サポート対象外)。
Debianはサポートしているので、そこからファイルをいただいて盛り込んでいく。
https://packages.debian.org/stable/admin/grub-efi-ia32-signed
https://packages.debian.org/bullseye/shim-signed
https://packages.debian.org/stable/admin/grub-efi-ia32-bin

ダウンロードしてくるファイルはリンクをたどって探すのが良いと思う。
この日は、このファイル名だった。
wget http://ftp.jp.debian.org/debian/pool/main/s/shim-signed/shim-signed_1.38+15.4-7_i386.deb
wget http://ftp.jp.debian.org/debian/pool/main/g/grub-efi-ia32-signed/grub-efi-ia32-signed_1+2.04+20_i386.deb
wget http://ftp.jp.debian.org/debian/pool/main/g/grub2/grub-efi-ia32-bin_2.04-20_i386.deb

mkdir bi32

dpkg-deb -x shim-signed_1.38+15.4-7_i386.deb bi32/
sudo cp --preserve=mode,timestamp bi32/usr/lib/shim/shimia32.efi.signed bi/EFI/BOOT/BOOTIA32.efi

dpkg-deb -x grub-efi-ia32-signed_1+2.04+20_i386.deb bi32/
sudo cp --preserve=mode,timestamps bi32/usr/lib/grub/i386-efi-signed/grubia32.efi.signed bi/EFI/BOOT/GRUBIA32.EFI

dpkg-deb -x grub-efi-ia32-bin_2.04-20_i386.deb bi32/
sudo cp --preserve=mode,timestamp -r bi32/usr/lib/grub/i386-efi bi/boot/grub/
sudo rm -r bi/boot/grub/i386-efi/monolithic/

※grub-efi-ia32-binに含まれていたmonolithic配下のefiのファイルは、署名されているファイルとmd5sum値が違っていたので消している。

[改変]できあがったEFI起動イメージをimageにコピーして、作業用ファイルをアンマウント。

sudo cp -ar bi/boot/ ../image/
sudo cp -ar bi/EFI/ ../image/
sudo umount bi
sudo chown 1000:1000 -R ../image/boot/ ../image/EFI/

※オーナー設定をUIDに変更。この作業をする人が1000ならこれでいけるはず。2022/01/16追記。

[改変]64ビットのブートローダーは/efi/boot/grub.cfgを見に行くので、この先で作るgrub.cfgを見に行くように仕掛けておく。

$HOME/live-ubuntu-from-scratch/image/EFI/BOOT/grub.cfg

#search.fs_uuid XXXX-XXXX root 
search --set=root --file /.disk/info
set prefix=(\$root)'/boot/grub'
configfile \$prefix/grub.cfg

[改変][オプション]32ビットのブートローダーは/efi/debian/grub.cfgを見に行くので、この先で作るgrub.cfgを見に行くように仕掛けておく。

cd $HOME/live-ubuntu-from-scratch/image
mkdir EFI/DEBIAN
cat << EOF > EFI/DEBIAN/grub.cfg
search --set=root --file /.disk/info
set prefix=(\$root)'/boot/grub'
configfile \$prefix/grub.cfg
EOF

[改変]ISOを作成するときに、起動イメージとして渡すefi.imgファイルを作成する。
[オプション]32ビットのブートローダーを利用する場合は、黄色の行を加える。

cd $HOME/live-ubuntu-from-scratch/image

通常
(
   dd if=/dev/zero of=efi.img bs=1M count=10 && \
   sudo mkfs.vfat efi.img && \
   LC_CTYPE=C mmd -i efi.img efi efi/boot boot boot/grub && \
   LC_CTYPE=C mcopy -i efi.img ./EFI/BOOT/grub.cfg ::boot/grub/ && \
   LC_CTYPE=C mcopy -i efi.img ./EFI/BOOT/BOOTX64.EFI ::efi/boot/ && \
   LC_CTYPE=C mcopy -i efi.img ./EFI/BOOT/grubx64.efi ::efi/boot/ && \
   LC_CTYPE=C mcopy -i efi.img ./EFI/BOOT/mmx64.efi ::efi/boot/ && \
   mv efi.img ./boot/grub/
)

32ビットのブートローダーをサポート
(
   dd if=/dev/zero of=efi.img bs=1M count=10 && \
   sudo mkfs.vfat efi.img && \
   LC_CTYPE=C mmd -i efi.img efi efi/boot efi/debian boot boot/grub && \
   LC_CTYPE=C mcopy -i efi.img ./EFI/DEBIAN/grub.cfg ::efi/debian/ && \
   LC_CTYPE=C mcopy -i efi.img ./EFI/BOOT/BOOTIA32.efi ::efi/boot/ && \
   LC_CTYPE=C mcopy -i efi.img ./EFI/BOOT/GRUBIA32.EFI ::efi/boot/ && \
   LC_CTYPE=C mcopy -i efi.img ./EFI/BOOT/grub.cfg ::boot/grub/ && \
   LC_CTYPE=C mcopy -i efi.img ./EFI/BOOT/BOOTX64.EFI ::efi/boot/ && \
   LC_CTYPE=C mcopy -i efi.img ./EFI/BOOT/grubx64.efi ::efi/boot/ && \
   LC_CTYPE=C mcopy -i efi.img ./EFI/BOOT/mmx64.efi ::efi/boot/ && \
   mv efi.img ./boot/grub/
)

Legacy(Grub)

[改変]Legacyで使うGrubイメージcore.imgを生成。EFI/BOOT/grub.cfgを指定しているのは、EFIと同じように/boot/grub/grub.cfgを参照したいから。

cd $HOME/live-ubuntu-from-scratch/image
grub-mkstandalone \
--format=i386-pc \
--output=isolinux/core.img \
--install-modules="linux16 linux normal iso9660 biosdisk memdisk search configfile tar ls" \
--modules="linux16 linux normal iso9660 biosdisk search configfile" \
--locales="" \
--fonts="" \
"/boot/grub/grub.cfg=EFI/BOOT/grub.cfg"

[改変]CDブート用のcdboot.imgとcore.imgを結合して、boot.imgを作成。出力先をboot/grub/に変更。
GNU GRUB Manual 2.06:Images / 11 GRUB image files
Qiita / Linuxのブートシーケンス

cat /usr/lib/grub/i386-pc/cdboot.img isolinux/core.img > boot/grub/boot.img

[改変]boot.imgを作成するために作成したcore.imgを削除。

rm isolinux/core.img

[改変]Grubの設定ファイルを作成。元々の手順ではimage/isolinuxにファイルを作成していたが、どうにも起動ディスクに仕立てられなかったので、image/boot/grubに作成している。

cat << EOF > boot/grub/grub.cfg
search --set=root --file /.disk/info

if loadfont /boot/grub/fonts/unicode.pf2 ; then
    set gfxmode=auto
    insmod efi_gop
    insmod efi_uga
    insmod gfxterm
    terminal_output gfxterm
else
    insmod all_video
fi

set default="0"
set timeout=30

menuentry "Try Ubuntu FS without installing" {
   linux /casper/vmlinuz boot=casper nopersistent toram quiet splash ---
   initrd /casper/initrd
}

menuentry "Install Ubuntu FS" {
   linux /casper/vmlinuz boot=casper only-ubiquity quiet splash ---
   initrd /casper/initrd
}

menuentry "Check disc for defects" {
   linux /casper/vmlinuz boot=casper integrity-check quiet splash ---
   initrd /casper/initrd
}

menuentry "Test memory Memtest86+ (BIOS)" {
   linux16 /install/memtest86+
}

menuentry "Test memory Memtest86 (UEFI, long load time)" {
   insmod part_gpt
   insmod search_fs_uuid
   insmod chain
   loopback loop /install/memtest86
   chainloader (loop,gpt1)/efi/boot/BOOTX64.efi
}
EOF

[改変]併せて、EFIの場合にGrubのメニューが呼び出されるように、/.disk/infoというファイルを作っておく。

mkdir .disk
echo "Ubuntu 20.04 from scratch" > .disk/info

Legacy(ISOLINUX)

[改変]USBに書き込んだときにLegacyでも起動できるHybrid ISOにするには、isolinuxが必要とのことで、そのための環境を作る。
このファイルがある・ないは直接関係ないとは思うのだけれども、Rufusというアプリケーションはこのファイルがある・ないでLegacy起動できる・できないを判断しているような節がある。
ISOLINUXを使ったISOを作った場合にも同じ選択肢が現れるように内容をそろえたが、Memtest86のISOは起動のさせ方が分からなかったので省いている。
Syslinux / Syslinux

cd $HOME/live-ubuntu-from-scratch/image
cat <<EOF> isolinux/isolinux.cfg
UI vesamenu.c32

MENU TITLE Boot Menu
DEFAULT linux
TIMEOUT 300
MENU RESOLUTION 640 480
MENU COLOR border       30;44   #40ffffff #a0000000 std
MENU COLOR title        1;36;44 #9033ccff #a0000000 std
MENU COLOR sel          7;37;40 #e0ffffff #20ffffff all
MENU COLOR unsel        37;44   #50ffffff #a0000000 std
MENU COLOR help         37;40   #c0ffffff #a0000000 std
MENU COLOR timeout_msg  37;40   #80ffffff #00000000 std
MENU COLOR timeout      1;37;40 #c0ffffff #00000000 std
MENU COLOR msg07        37;40   #90ffffff #a0000000 std
MENU COLOR tabmsg       31;40   #30ffffff #00000000 std

LABEL linux
 MENU LABEL Try Ubuntu FS
 MENU DEFAULT
 KERNEL /casper/vmlinuz
 APPEND initrd=/casper/initrd boot=casper nopersistent toram quiet splash ---

LABEL linux
 MENU LABEL Install Ubuntu FS
 MENU DEFAULT
 KERNEL /casper/vmlinuz
 APPEND initrd=/casper/initrd boot=casper only-ubiquity quiet splash ---

LABEL linux
 MENU LABEL Check disc for defects
 MENU DEFAULT
 KERNEL /casper/vmlinuz
 APPEND initrd=/casper/initrd boot=casper integrity-check quiet splash ---

LABEL memtest
 MENU LABEL Memtest86+
 LINUX ../install/memtest86+
EOF

必要モジュールをisolinuxディレクトリにコピー。

cp -a /usr/lib/ISOLINUX/isolinux.bin isolinux/
cp -a /usr/lib/syslinux/modules/bios/* isolinux/

ISOイメージの作成

[改変]md5sum.txtファイルの作成。起動時のファイルチェックでエラーが発生するため、4つのファイルを外してある。

cd $HOME/live-ubuntu-from-scratch/image
sudo /bin/bash -c "(find . -type f -print0 | xargs -0 md5sum | grep -v -e 'md5sum.txt' -e 'boot.img' -e 'efi.img' -e 'isolinux.bin' > md5sum.txt)"

[改変]ISOイメージを作成。
IOSS / 資料3: mkisofs(8) ver.2.0 man page [日本語]
debian manpages / XORRISOFS
技術評論社 / 第93回 xorrisoとUEFIブート再び[その3]

sudo xorriso \
   -as mkisofs \
   -iso-level 3 \
   -full-iso9660-filenames \
   -volid "UBUNTU_FROM_SCRATCH" \
   -output ../ubuntu-from-scratch.iso \
   -isohybrid-mbr /usr/lib/grub/i386-pc/boot_hybrid.img \
   --grub2-mbr /usr/lib/grub/i386-pc/boot_hybrid.img \
   --grub2-boot-info \
   -b boot/grub/boot.img \
      -no-emul-boot \
      -boot-load-size 4 \
      -boot-info-table \
      --eltorito-catalog boot/grub/boot.cat \
   -append_partition 2 0xef boot/grub/efi.img \
   -eltorito-alt-boot \
      -e boot/grub/efi.img \
      -no-emul-boot \
      -isohybrid-gpt-basdat \
   ./

LegacyでISOLINUXを利用する場合はこちら。

sudo xorriso \
   -as mkisofs \
   -iso-level 3 \
   -full-iso9660-filenames \
   -volid "UBUNTU_FROM_SCRATCH" \
   -output ../ubuntu-from-scratch.iso \
   -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin \
   -b isolinux/isolinux.bin \
      -no-emul-boot \
      -boot-load-size 4 \
      -boot-info-table \
      --eltorito-catalog isolinux/isolinux.cat \
      --grub2-boot-info \
   -append_partition 2 0xef boot/grub/efi.img \
   -eltorito-alt-boot \
      -e boot/grub/efi.img \
      -no-emul-boot \
      -isohybrid-gpt-basdat \
   ./

Ubuntu 20.04のLive CDにはisolinuxディレクトリがあって何かが書き込まれているのだけれども、どのようなケースがどんな方法でカバーされているのかがよく分からず、同じものを作ることができなかった。
Grubでみてみると、(cd0,apple1)とか(cd0,apple2)があったので、そうしたデバイスにも対応していると言うことなのかもしれないが、今回はここまで。

さて、できあがったISOイメージを確認。

ll -h ../ubuntu-from-scratch.iso
-rw-r--r-- 1 root root 2.6G 12月 31 23:01 ../ubuntu-from-scratch.iso

思ったよりもサイズがでかい…途中で結構色々とインストールしたので、当然か。

ISOを取り出して試す

できあがったISOイメージを取り出すのに、VMwareの共有フォルダの仕組みを使う。

sudo mount -t fuse.vmhgfs-fuse -o allow_other .host:/VMware /mnt
cp ../ubuntu-from-scratch.iso /mnt/
sudo umount /mnt

最初はセキュアブートができなかったが、手順をいくつか改変したことで起動できるようになっている。
また、Legacyでも、32bit UEFI+セキュアブートでも問題なく起動する。

動作を確認してみる。

  • 起動時のファイルチェックでエラーが発生しなかった。
    以前、ディスクイメージのカスタマイズに挑戦したときは、1つのファイルでエラーが発生していた。
  • 全体に問題なくLive起動できている。
    • UFWはインストールされており、非アクティブ。
    • AppArmorはインストールされているが起動していない。
    • ネットワークは、普通に使えている。
      • 仮想ホストのネットワークアダプタが使える。
      • Wi-Fiアダプタ搭載のタブレットで、問題なくアクセスポイントに接続できた。
    • 音は鳴る。
    • 日本語入力のために、画面右上のjaのところをクリックして、日本語→日本語(Mozc)への変更が必要。
  • カスタマイズしたとおりにアプリケーションがインストールされている。
    • LibreOfficeが入っていて、麻雀は入っていない。
    • open-vm-toolsはインストールされていない。
  • メモリに応じた動作ができている。
    • 8GBのメモリを割り当てた仮想ゲストの場合、起動に必要なファイルがすべてメモリに展開される。
      freeコマンドの結果available(割り当て可能なメモリ)が3.9GB。
      Liveが入ったデバイスを取り外すことができる。
    • 2GBのメモリしかないタブレットでは、メモリへの展開ができないので、都度デバイスからファイルを読み出すようだ。
      当然、動作中にLiveが入ったデバイスを取り出してしまうと、エラーが発生する。

といったところ。全体に問題なく動いている。

手順は簡単とは言えないけれど、スクラッチでここまでのLive環境ができるのは凄い。

USBフラッシュメモリーへの書き込み

Windows

WindowsにはRufusという便利なツールがあるので、これを使って書き込めば良い
このツールは、書き込みたいISOイメージを選択した段階で、BIOSやUEFIといった起動対応デバイスを教えてくれる。
今回の手順を実行し、BIOSまたはUEFIと表示されれば、どの環境でも起動できるようになっていると思われる。

今回の手順で作成したイメージの起動を確かめてみた。

起動方法Rufus(ISOイメージモード)Rufus(ddイメージモード)Ubuntu dd参考:CD起動
UEFI 64bit(VMware)
UEFI 64bit(現在のメインPC)未テスト
UEFI 32bit(Miix 2 8)未テスト
Legacy(VMware)
Legacy(Acerのかなり古いPC)◯*S◯*S◯*S未テスト

*S: SYSLINUXでも起動が確認できた。

RufusでいうところのISOイメージモードでは、EFIパーティションから起動しているように見えた。
USBフラッシュメモリーの空き領域にファイルを書き込むことができる。

ddイメージモードでは、efi.imgやboot.img、SYSLINUXから起動しているように見えた。
こちらは特殊なフォーマット状態になり、空き領域0で何も書き込むことができない。

今回の手順では、メニューを定義しているgrub.cfgを1つ用意し、他の場所に置いたgrub.cfgはすべてそれを参照するようにした。
これらを作らないでISOイメージを作って実験してみれば、Grubでメニューが表示されずコマンドプロンプトが表示されるので、なんとなくどこから起動しているのかが分かってくると思う。
コマンドプロンプトが表示されたら、lsコマンドでメニューを定義しているgrub.cfgを探して、configfile (hd0,msdos2)/boot/grub/grub.cfgコマンドでメニュー表示されるので、問題発生時に試してみると、どこが悪いのかを特定できるかもしれない。

Ubuntu

ddコマンドを使って、ISOイメージを書き込むことができる。
この場合は、Rufusのddイメージモードと同様、特殊なフォーマット状態になって、読み込みはできるが書き込めない状態になる。

sudo dd if=./ubuntu-from-scratch.iso of=/dev/sdb bs=1M status=progress oflag=sync

※書き込み先のデバイスに注意。

書き込み対象となるデバイスは lsblk コマンドで大体分かると思う。

live-helperを使ってみる

Liveってなんなのか?というところを知るには、作ってみるのが一番だろうと思って探してみた。
技術評論社 / 第113回 Debian Liveのlive-helperを使ってUbuntu Liveを作成する
技術評論社 / 第114回 Debian Liveのlive-helperを使ってUbuntu Liveを作成する (2)

記事は2010年のもので、Ubuntu9.10ベースで書かれている。

テスト環境の構築

同じバージョンでないと、同じ動きはしないかもしれない…ということで、9.10をダウンロードしてくる。
Old Ubuntu Releases
※今となっては古いやつなのでダウンロード帯域に制限が掛かっているようで、ISOイメージ自体は700MB弱だけれども1時間程掛かるので、試すのであれば先にダウンロードしておくことをオススメ。

テストにはVMware Workstation 16 Player 16.2.1を利用させていただく。
英語でインストール。Ubuntu 12.04から経験し始めていたのだけれど、この時代から既になんか格好いい。

ログインサウンドもなんか格好いいんですけど。

[CTRL]+[ALT]+Tでターミナルが開かないので、メニューからターミナルを開いて、アップデートを実行。

$ sudo apt-get update

404が返ってくる。
Qiita / Ubuntu で apt-get update が404になる問題
ask ubuntu / Could not resolve ‘id.old-releases.ubuntu.com’ [duplicate]

$ sudo sed -i.001 -e 's/archive.ubuntu.com\|security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list
$ sudo sed -i.002 -e 's/jp.old-/old-/g' /etc/apt/sources.list

※バックアップを取ってから、old-releasesに置き換えている。

アップデートできた。

$ sudo apt-get update
…
Reading package lists... Done

この先の操作を楽にするため、open-vm-toolをインストールしてみる。クリップボードの共有はできるだろうか?

$ sudo apt-get install open-vm-tools open-vm-toolbox

インストール直後はクリップボードの共有ができなかった。
だけれども、一度再起動してみたら、クリップボードの共有ができるようになっていた。
また、ウィンドウのサイズを広げると、追従してデスクトップが広がる。うん、いい感じ!

ただ、これだけでは、ホストで共有フォルダを用意してもアクセスができなかった。
LANGUI.SH / Ubuntu 9.10 In VMware – Updated

$ sudo m-a

このコマンドで、module-assistantが起動する。

再起動後に確認してみたところ、教えてくれたサイトの確認結果と完全に一致。

$ lsmod | grep vm
vmsync                  5104  0 
vmmemctl               10120  0 
vmhgfs                 59080  0 
vmci                   33952  0 

これで共有フォルダがマウントできるようになった。

さて、パッケージ全体のアップグレード。アクセス速度は256KB/s程度となっており、30分ぐらいかかる。

$ sudo apt-get dist-upgrade
…
After this operation, 238MB of additional disk space will be used.
Do you want to continue [Y/n]?

この段階でハードディスクイメージをバックアップした。
サイズは3.3GB程だった。

live-helperを使ってみる

教えてくれる手順に沿ってコマンドを実行してみる。

$ sudo apt-get install live-helper
$ mkdir -p work/live
$ cd work/live

実行にあたり、リポジトリを書き換えて実行。

$ lh_config \
--mode ubuntu \
--mirror-bootstrap "http://old-releases.ubuntu.com/ubuntu/" \
--mirror-chroot "http://old-releases.ubuntu.com/ubuntu/" \
--mirror-chroot-security "http://old-releases.ubuntu.com/ubuntu" \
--mirror-binary "http://old-releases.ubuntu.com/ubuntu/" \
--mirror-binary-security "http://old-releases.ubuntu.com/ubuntu"

設定ファイルができるとのことで、この後でISOイメージを作成。

$ sudo lh_build
P: Setting up cleanup function
P: Begin caching bootstrap stage...
P: Begin bootstrapping system...
P: If the following stage fails, the most likely cause of the problem is with your mirror configuration or a caching proxy.
P: Running debootstrap (download-only)... 
I: Retrieving Release
I: Retrieving Packages
I: Validating Packages
…
Hit http://old-releases.ubuntu.com jaunty-security/restricted Packages
Reading package lists... Done
P: Deconfiguring file /etc/hostname
P: Deconfiguring file /bin/hostname
P: Deconfiguring file /etc/resolv.conf
P: Deconfiguring file /etc/hosts
P: Begin unmounting filesystems...

当然ながら、これにも時間は掛かったが、できあがった。137MB!

$ ls -lah
total 137M
drwxr-xr-x  8 rohhie rohhie 4.0K 2021-12-04 09:37 .
drwxr-xr-x  3 rohhie rohhie 4.0K 2021-12-04 09:22 ..
drwxr-xr-x  5 root   root   4.0K 2021-12-04 09:37 binary
-rw-r--r--  1 root   root   137M 2021-12-04 09:37 binary.iso
-rw-r--r--  1 root   root    567 2021-12-04 09:37 binary.list
-rw-r--r--  1 root   root    34K 2021-12-04 09:37 binary.packages
drwxr-xr-x  6 root   root   4.0K 2021-12-04 09:37 cache
drwxr-xr-x 20 root   root   4.0K 2021-12-04 09:37 chroot
drwxr-xr-x 22 rohhie rohhie 4.0K 2021-12-04 09:22 config
drwxr-xr-x  2 rohhie rohhie 4.0K 2021-12-04 09:22 scripts
drwxr-xr-x  2 rohhie rohhie 4.0K 2021-12-04 09:37 .stage

できあがったISOイメージを取り出すのに、VMwareの共有フォルダの仕組みを使う。

$ mkdir ~/work/VMware
$ sudo mount -t vmhgfs .host:/VMware /home/rohhie/work/VMware/ -o uid=1000,gid=1000

取り出したISOをVMwareにマウントして起動してみると…

ということで、思ったように起動はしなかったけれど、この後で[CTRL]+[ALT]+[DELETE]を送信したところ、コンソールのまま、ubuntuでログインした状態になった。

IPアドレスは払い出されていないけれど、いくつかのサービスは動作しているようだった。

作業ディレクトリをクリアするときには、以下のコマンドを利用するとのこと。
確かに、設定ファイルがまるごと消えてしまっては困る。

$ sudo lh_clean

このコマンドは、次のMakefileにも書かれていた。

軽量デスクトップの作成

教えてくれているとおりに軽量デスクトップを作成してみる。
作業ディレクトリは ~/work/live。

~/work/live/scripts/config

#!/bin/sh

MIRROR_UBUNTU="http://old-releases.ubuntu.com/ubuntu/"
MIRROR_SECURITY="http://old-releases.ubuntu.com/ubuntu/"

lh_config noautoconfig \
        --mode ubuntu \
        --distribution karmic \
        --language ja \
        --mirror-binary ${MIRROR_UBUNTU} \
        --mirror-binary-security ${MIRROR_SECURITY} \
        --mirror-bootstrap ${MIRROR_UBUNTU} \
        --mirror-chroot ${MIRROR_UBUNTU} \
        --mirror-chroot-security ${MIRROR_SECURITY} \
        --categories "main restricted universe multiverse" \
        ${@}

※赤文字は改変箇所。

~/work/live/scripts/build

#!/bin/sh

lh_build noautoconfig 2>&1 | tee binary.buildlog

~/work/live/scripts/clean

#!/bin/sh

lh_clean noautoconfig --all ${@}

# Remove generated files
rm -f config/binary config/bootstrap config/chroot config/common config/source

# Remove empty directories in config tree
if ls config/*/ > /dev/null 2>&1
then
        rmdir --ignore-fail-on-non-empty config/*/
fi

if [ -d config ]
then
        rmdir --ignore-fail-on-non-empty config
fi

実行権限を付ける。

$ chmod +x scripts/*

※実行権限を付けておかないと、スクリプトが実行できない。エラーが発生しているのが分からないし、パッケージをJauntyからダウンロードしたりする。

これをmakeで実行するためのMakefileを作成。

~/work/live/Makefile

BOOTOPTION_LIVE = quiet splash debian-installer/language=ja console-setup/layoutcode?=jp console-setup/modelcode?=jp106 

all: config build

config: clean
	lh_config \
        --bootloader grub \
        --binary-images iso \
        --bootappend-live "$(BOOTOPTION_LIVE)" \
        --packages "lxde language-pack-ja language-support-ja usplash-theme-ubuntu ubiquity-frontend-gtk gparted"

build: 
        sudo lh_build 

clean:
        sudo lh_clean
        sudo rm -f *.buildlog

※行頭8このスペースは、TABコードに置き換える。

実行してみたところ、大体20分くらいで終了した。

$ make all
sudo lh_clean
P: Cleaning chroot
sudo rm -f *.buildlog
lh_config \
	--bootloader grub \
	--binary-images iso \
	--bootappend-live "quiet splash debian-installer/language=ja console-setup/layoutcode?=jp console-setup/modelcode?=jp106 " \
	--packages "lxde language-pack-ja language-support-ja usplash-theme-ubuntu ubiquity-frontend-gtk gparted"
sudo lh_build
P: Setting up cleanup function
…
Reading package lists... Done
P: Deconfiguring file /etc/hostname
P: Deconfiguring file /bin/hostname
P: Deconfiguring file /etc/resolv.conf
P: Deconfiguring file /etc/hosts
P: Begin unmounting filesystems...

結果を確認してみる。

$ ls -lah
total 320M
drwxr-xr-x  8 rohhie rohhie 4.0K 2021-12-04 17:28 .
drwxr-xr-x  4 rohhie rohhie 4.0K 2021-12-04 17:09 ..
drwxr-xr-x  5 root   root   4.0K 2021-12-04 17:27 binary
-rw-r--r--  1 root   root   276K 2021-12-04 17:27 binary.buildlog
-rw-r--r--  1 root   root   320M 2021-12-04 17:27 binary.iso
-rw-r--r--  1 root   root    315 2021-12-04 17:27 binary.list
-rw-r--r--  1 root   root    78K 2021-12-04 17:27 binary.packages
drwxr-xr-x  6 root   root   4.0K 2021-12-04 17:06 cache
drwxr-xr-x 20 root   root   4.0K 2021-12-04 17:27 chroot
drwxr-xr-x 22 rohhie rohhie 4.0K 2021-12-04 17:06 config
-rw-r--r--  1 rohhie rohhie  431 2021-12-04 16:41 Makefile
drwxr-xr-x  2 rohhie rohhie 4.0K 2021-12-04 17:09 scripts
drwxr-xr-x  2 rohhie rohhie 4.0K 2021-12-04 17:28 .stage

できあがったISOをホストに取り出す。

$ sudo mount -t vmhgfs .host:/VMware /home/rohhie/work/VMware/ -o uid=1000,gid=1000
$ cp binary.iso /home/rohhie/work/VMware/ubuntu-9.10-original-live.iso

起動してみたところ、こんな感じ。

画面の文字が日本語、キーボードも日本語配列、とても軽い。
UFWやAppArmorは入っていないけれど、この方法ならば後から入れていけば良いのだろう。

記事を読み進めると、USB空起動できるシステムの作り方も書かれていて、この分なら何の問題もなく動作することだろう。

2010年には既にこんなことができる人がいて、記事を書いていたんだと思うと、素晴らしいことだなぁと思うのだった。

やったこと

ブートについて

Legacyの場合…
MBRの小さなプログラム(Master bootstrap loader)が読み込まれる
→ 起動パーティションの先頭セクターにあるプログラム(Initial Program Loader)が読み込まれる
→ OSがロードされる
という流れでOSが起動している(ざっくり過ぎるけど)。
Nobusan’s Square / ブートの仕組み

UEFIはどうかというと、起動にはESP(EFI System Partition)が使われていて…
起動するディスク・パーティションを決定
→ ファームウェアのブートエントリーに登録されているUEFIアプリケーションを起動
→ bootx64.efiが起動され、mmx64.efiを使ってMOKを取り扱いつつ、grubx64.efiを起動
→ Grub2で選択されたOSがロードされる
という流れと思われた(これも超ざっくり)。
ArchWiki / Unified Extensible Firmware Interface
ArchWiki / GRUB
ArchWiki / セキュアブート

UEFIには32ビットと64ビットがあって、古いものだと32ビットだったりする。
(持っているデバイスでいうと、Miix 2 8は32ビット)

起動するUEFIアプリケーションは /EFI/boot/bootx64.efi(64ビット) 固定の場合もあると書かれていた。

拡張子efiのファイルがいくつかあるのだが、それぞれがどんな機能を持つのか調べてみた結果がこちら。

ファイル名機能
bootx64.efiブートローダー。EFIはこれを探して起動する。
取り扱うOSによるが、Ubuntuの場合はGrub2を実行しようとする。
grubx64.efiGrub2ブートローダー本体(っぽい)。
shimx64.efiブートローダー。起動時にgrubx64.efiを実行しようとする。
ハッシュまたは署名鍵が見つからない場合、mmx64.efiを起動する。
インストール済みのUbuntu 20.04で確認してみたら、bootx64.efiとmd5sumの結果が一致していた。
mmx64.efiMokManager.efiから名前が変わったらしい。
MOKを管理するためのツール
fbx64.efiGrubを起動する。

起動させるためのテクニックとして、shimx64.efiをbootx64.efiという名前で置いたりなんかもするようなので、いま、Liveシステムに入っているファイルがなんなのか良くわからなかったりもするが、これらを考慮してISOを作れば、ハイブリッドな感じで起動させられそうだ。

ディレクトリ構成

今回教えてもらった手順でいくと、casper、install、isolinuxというディレクトリができる。
それぞれなんのためのディレクトリなのか。

ディレクトリ機能
casperCasperはmake-liveで作成されたLiveシステムを起動する仕組み
カーネルパラメーターにboot=casperが指定されていると、/casperからsquashfsファイルを探す。
見つかればunionfsを使って書き込み可能な環境を作ってシステムを起動する。
変更は保存されない
installUbuntu 20.04.03にはmt86plus(179KB)が入っていた。
今回の手順ではmemtest86(500MB!!)とmemtest86+(179KB)が入っている。
mt86plusとmemtest86+はmd5sum値が一致していたので、同じファイル。
memtest86はloopbackでマウントした後chainloadしているので、ISOイメージと思われる。
isolinuxエミュレーションなしのモードでISO9660/El Toritoで動作するi386用のブートローダーを入れておく場所。
Ubuntu 20.04.03には大量にファイルが入っているのだけれど、isolinux.binとisolinux.cfg、boot.catが確かに入っていた。
たくさん入っているファイルについては、ここをたどっていけば見つかるかもしれない

casperはLiveシステムが起動したときに、システムそのものになるディレクトリのようだ。
また、色々なシステムで起動できるようにするには、isolinuxを理解しておく必要がありそう。
軽量なISOを作るなら、memtest86(500MB!!)は外しても良さそう。

セキュアブートしているかどうか確認

現在起動しているOSがセキュアブートしているのかどうか確認してみようと考えた。
こういうことは、たいがいArch Linuxが教えてくれる。
ArchWiki / セキュアブート

Ubuntu 20.04.3 のインストールディスクから起動した状態では以下の通り。

$ sudo bootctl status
Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.
Alternatively, use --esp-path= to specify path to mount point.
System:
     Firmware: n/a (n/a)
  Secure Boot: enabled
   Setup Mode: user

Current Boot Loader:
      Product: n/a
     Features: ✗ Boot counting
               ✗ Menu timeout control
               ✗ One-shot menu timeout control
               ✗ Default entry control
               ✗ One-shot entry control
               ✗ Support for XBOOTLDR partition
               ✗ Support for passing random seed to OS
               ✗ Boot loader sets ESP partition information
          ESP: n/a
         File: └─n/a

Random Seed:
 Passed to OS: no
 System Token: not set

Boot Loaders Listed in EFI Variables:
        Title: ubuntu
           ID: 0x0003
       Status: active, boot-order
    Partition: /dev/disk/by-partuuid/c7076d24-d43c-4fe9-95dd-958ffa481a48
         File: └─/EFI/ubuntu/shimx64.efi

セキュアブートが有効になっている。

ISOの構造を調べる

USBフラッシュメモリーにddで書き込んだISOイメージがLegacyで起動しなかったことで、ディスクの構造を確認する必要が出てきた。
結論からすると、DVDから立ち上げた場合にはある程度優しく読んでくれるようだが、ISOイメージをddでUSBフラッシュメモリーに書き込んだ場合、HDDとして取り扱われるため、MBRにブートローダーを仕掛けておく必要があるようだ。

さて…通常、ディスクを入れると最初のパーティションが表示されて、その後ろにあるパーティションが見えない。
Man On a Mission / 【Linux】kpartxでループバックデバイスを簡単にマウントする

kpartxというコマンドでISOイメージのパーティションをマッピングしてくれる。

sudo kpartx -av ubuntu-from-scratch.iso 
add map loop14p1 (253:0): 0 5376628 linear 7:14 0
add map loop14p2 (253:1): 0 20480 linear 7:14 5376628

lsblk
NAME       MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
<omit>
loop14       7:14   0   2.9G  0 loop 
├─loop14p1 253:0    0   2.9G  0 part 
└─loop14p2 253:1    0   3.9M  0 part 
sda          8:0    0    40G  0 disk 
├─sda1       8:1    0   512M  0 part /boot/efi
└─sda2       8:2    0  39.5G  0 part /
sr0         11:0    1   2.6G  0 rom  

ll /dev/mapper/
total 0
drwxr-xr-x  2 root root     100 12月 31 06:45 ./
drwxr-xr-x 20 root root    4280 12月 31 06:45 ../
crw-------  1 root root 10, 236 12月 30 10:47 control
lrwxrwxrwx  1 root root       7 12月 31 06:45 loop14p1 -> ../dm-0
lrwxrwxrwx  1 root root       7 12月 31 06:45 loop14p2 -> ../dm-1

適当なディレクトリを作ってマウントしてみたところ、確かに中身を確認することができる。

sudo mount /dev/mapper/loop14p1 mp/p1
sudo mount /dev/mapper/loop14p2 mp/p2

確認が終わったら、以下で解放。

sudo umount mp/p1
sudo umount mp/p2
sudo kpartx -d ubuntu-from-scratch.iso
loop deleted : /dev/loop14

ということで、Ubuntu 20.04.3 desktopがどのような構造なのかを確認してみたところ、2つ目のパーティションにあるファイルは、今回の手順で作成したものと大差がなかった。

もう少し何か手がかりはないか…
技術評論社 / 第93回 xorrisoとUEFIブート再び[その3]

file /mnt/ubuntu-20.04.3-desktop-amd64.iso ../ubuntu-from-scratch.iso
/mnt/ubuntu-20.04.3-desktop-amd64.iso: DOS/MBR boot sector; partition 2 : ID=0xef, start-CHS (0x3ff,254,63), end-CHS (0x3ff,254,63), startsector 5271500, 8000 sectors
../ubuntu-from-scratch.iso:            DOS/MBR boot sector, code offset 0x3c+2, OEM-ID "mkfs.fat", sectors/cluster 4, reserved sectors 4, root entries 512, sectors 20480 (volumes <=32 MB), Media descriptor 0xf8, sectors/FAT 20, sectors/track 32, heads 64, serial number 0x63abb652, unlabeled, FAT (16 bit)

fdisk -l /mnt/ubuntu-20.04.3-desktop-amd64.iso
Disk /mnt/ubuntu-20.04.3-desktop-amd64.iso: 2.88 GiB, 3071934464 bytes, 5999872 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
Disklabel type: dos
Disk identifier: 0x2cf4ba3a

Device                                 Boot   Start     End Sectors  Size Id Type
/mnt/ubuntu-20.04.3-desktop-amd64.iso1 *          0 5999871 5999872  2.9G  0 Empty
/mnt/ubuntu-20.04.3-desktop-amd64.iso2      5271500 5279499    8000  3.9M ef EFI (FAT-12/16/32)

fdisk -l ../ubuntu-from-scratch.iso
Disk ../ubuntu-from-scratch.iso: 2.59 GiB, 2765549568 bytes, 5401464 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
Disklabel type: dos
Disk identifier: 0x3804a073

Device                      Boot   Start     End Sectors  Size Id Type
../ubuntu-from-scratch.iso1 *          0 5380319 5380320  2.6G  0 Empty
../ubuntu-from-scratch.iso2      5380320 5400799   20480   10M ef EFI (FAT-12/16/32)

fdiskで見た結果はあまり変わらない。

そこでISOLINUXによるハイブリッド起動を試したところ、問題なく起動ができることを確認。
MBRに何を書くかで決まることが分かって、今回の手順が整理できた。

起きたこと

セキュアブート環境でエラー

セキュアブート環境で、エラーが発生してGrubが上手く動作しなかった。

そこで、仮想ゲストにCDをマウントしてGrubで中身を色々みようとしていたところで、このような表示が…
これ以降、このディスクは反応しなくなってしまう。

grub> ls
(memdisk) (hd0) (cd0) (cd0,msdos2)

grub> ls (cd0,msdos2)/boot/grub/x86_64-efi/
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/ufs2.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/ufs1_be.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/ufs1.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/udf.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/tar.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/sfs.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/romfs.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/reiserfs.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/procfs.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/odc.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/nilfs2.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/minix_be.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/minix3_be.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/minix3.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/minix2_be.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/minix2.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/minix.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/jfs.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/hfs.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/f2fs.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/exfat.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/cpio_be.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/cpio.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/cbfs.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/bfs.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/afs.mod.
error: Secure Boot forbids loading module from (cd0)/boot/grub/x86_64-efi/affs.mod.
error: unknown filesystem.

grub> ls (cd0,msdos2)/boot/grub/x86_64-efi/
error: unknown filesystem.

これについては、こちらで教えてくれた。
UNIX & LINUX / Signing grub for secureboot uefi: Secureboot forbids to load module (memdisk)/boot/grub/x86_64-efi/normal.mod

使えるモジュールが限られている、ということだった。

ということで、エラー表示されたファイルを、efi.imgの中から消してみる。

$ sudo mount ${HOME}/live-ubuntu-from-scratch/image/boot/grub/efi.img /mnt
$ cd /mnt
$ sudo rm ufs2.mod ufs1_be.mod ufs1.mod udf.mod tar.mod sfs.mod romfs.mod reiserfs.mod procfs.mod odc.mod nilfs2.mod minix_be.mod minix3_be.mod minix3.mod minix2_be.mod minix2.mod minix.mod jfs.mod hfs.mod f2fs.mod exfat.mod cpio_be.mod cpio.mod cbfs.mod bfs.mod afs.mod affs.mod
$ cd ${HOME}/live-ubuntu-from-scratch/image
$ sudo umount /mnt

ところが、消しても同じエラーが出るし、さらにいえば、セキュアブートではない環境では「これらのファイルがない」と怒られるのだった。
リストでもあるの?と探してみると、fs.1stというファイルがあり、そこにほぼこれらがリストアップされたファイルがある。これは…

手詰まりとなり、Ubuntu 20.04のディスクで同じ操作をしてみたところ、同じようにunknown filesystemになってしまった。
ブートできない原因はこれじゃないと分かったので検討していって、署名されたGrubをインストールすることにした。

rror: file `/boot/` not found.

UEFI(64bit)+セキュアブートの環境、ddでUSBフラッシュメモリーに書き込んだイメージで起動したところ、一瞬このメッセージが表示された後でメニューが表示される。

rror: file `/boot/` not found.
error: no such device: /.disk/info
error: no such device: /.disk/mini-info

※最初のeが表示されていないのは何らかの都合じゃないかと。ビデオ撮影して何度か確認したので、間違いはなさそう。

ちゃんと起動するからいいじゃん?という話はあるものの、どういうことなのかが少し気になったので調べてみた。

まず、/boot/がない件。

最初に試していた手順では、efi.imgの中に/boot/grub/grub.cfgを置いていなかった。
最初にそれを見に行くのでこのエラーを表示していた。

このファイルを置いたことで解決できた。

次に、/.disk/infoの件。

EFIイメージの場合には常に埋め込まれている文字があるらしく、それが表示されているようだ。
stackoverflow / How does the GRUB2 UEFI loader know where to look for the configuration file (or where the 2nd stage’s files are located)?

実際に、Grubメニューでちょっと確認してみた。
この場合、(hd1)がUSBフラッシュメモリーそのものを表していて、(hd1,msdoc1)がISOイメージの中身らしい。

grub> ls
(memdisk) (hd0) (hd1) (hd1,msdos1) (hd2)

grub> ls (hd1,msdos1)/
DIR      System Volume Information/
69145    ldlinux.sys
DIR      boot/
DIR      casper/
DIR      efi/
<omit>

grub> ls (hd1,msdos1)/efi/
DIR      boot/
DIR      debian/

grub> echo $root
hd1,msdos1

では、なぜ最初のメッセージが表示されるのか。

grub> cat (memdisk)/grub.cfg
if [ -z "$prefix" -o ! -e "$prefix" ]; then
        if ! search --file --set=root /.disk/info; then
                search --file --set=root /.disk/mini-info
        fi
        set prefix=($root)/boot/grub
fi
if [ -e $prefix/x86_64-efi/grub.cfg ]; then
        source $prefix/x86_64-efi/grub.cfg
elif [ -e $prefix/grub.cfg ]; then
        source $prefix/grub.cfg  → echoしてみたところ (hd1,msdos1)/boot/grub
else
        source $cmdpath/grub.cfg → echoしてみたところ (hd1,msdos1)/EFI/BOOT
fi

ということで、固定的に /.disk/info というファイルを探し、見つけたパーティションをrootにする。
そして、そのディスクの /boot/grub/grub.cfgを読み出す、といった形で起動しているようだ。

sourceコマンドというのは、ファイルを読み込んで実行するコマンド。
configfileコマンドの場合は、読み込んでメニューを即座に表示するが、sourceの場合はメニューは後から表示されるといったことが書かれている。

いずれにしても、こういう固定的な設定があるのなら、これに載っかるのが得策のように思われる。

なお、このファイルを置いたところ、最初の2単語がインストーラー表示名の先頭に表示された。

さいごに

今回は、過去のUbuntuを利用してみて、当時から素晴らしいものだったのだなぁと思ったのと、Makefileってほとんど使ったこともなかったのだけれど、これはこれでやっぱりかなり使いやすいものなんだと思ったのとで、改めてやってみて良かった。

その後で、Ubuntu 20.04の起動ディスク作成に取り組んだが、これが本当に色々とあって、まともにOSが起動してくるまでが大変だった。
起動には色々なルートがあるし、DVDに焼き込んだときと、USBフラッシュメモリーに書き込んだときとで、BIOSからの見え方が変わって動作が変わる。BIOSが優秀だと色々と察してくれたりするので、察してくれないタイプのBIOSでは設定が甘いと上手く起動できなくなったりもする。

なんとかそれっぽく起動するようになったとは思うが、お気づきの点があれば、是非教えてください。

広告

コメントはこちらから お気軽にどうぞ ~ 投稿に関するご意見・感想・他