ブータブルLinuxISOを手作りする

Linuxのブータブルディスクの仕組みが気になったので手作りする方法を調べてみました。

ちなみにRedHat系OSであればloraxパッケージに含まれるlivemedia-creatorコマンドで簡単に作成できます。[1]

```
# livemedia-creator --make-iso --ks=/path/to/valid/kickstart.ks --no-virt
```

# ブータブルISOについて

## ISOの規格

ご存知のとおり、CD/DVDのフォーマットはISO9660で標準化されていますが、オリジナルの規格はファイル名等の制約が厳しいため様々な拡張規格が定義されています。
ISOファイルからのシステムブートはEl Toritoという拡張規格で定義されています。[2]

## Linuxのブータブルディスク

LinuxシステムをISOからブートするにはISOLINUXという軽量ブートローダーを利用します。ISOLINUXはSYSLINUXに含まれるコンポーネントの一つです。

SYSLINUXはLinux向けの軽量ブートローダーを提供するプロジェクトで、いくつかのコンポーネントを含んでいます。例えばSYSLINUXコンポーネントはFAT32ファイルシステムから、PXELINUXはネットワークからシステムをブートするためのブートローダーを提供しています。[3]

例としてCentOS7のインストーラを見てみます。
CentOS7のインストーラーもCD/DVDからブートするLinuxシステムです。

インストーラーISOマウントすると(もしくはCentOSミラーの os/x86_64/ ディレクトリを開くと)isolinuxというディレクトリが含まれています。このディレクトリにはブートローダーである isolinux.bin、ブートメニューや起動オプション等を指定するisolinux.cfg、そのほかカーネルイメージvmlinuzや初期RAMディスクinitrd.imgが含まれています。

```
http://one.of.a.mirror/centos/7/os/x86_64/isolinux/
 boot.msg	2018-05-03 20:34	84	 
 grub.conf	2018-05-03 20:34	281	 
 initrd.img	2018-05-03 20:34	50M	 
 isolinux.bin	2018-05-03 20:34	24K	 
 isolinux.cfg	2018-05-03 20:34	3.0K	 
 memtest	2016-11-05 16:27	186K	 
 splash.png	2015-09-30 19:58	186	 
 vesamenu.c32	2016-11-05 17:41	149K	 
 vmlinuz	2018-04-20 16:57	5.9M	 
```

ちなみにルートファイルシステムは LiveOS/squashfs.img にあります。
このイメージはsquashfsという圧縮された読み取り専用のファイルシステムです。
イメージの中には LiveOS/rootfs.img が含まれていて、これがルートファイルシステムのイメージファイルになります。
ルートファイルシステムにはAnaconda(Pythonの環境構築ツールではありません)というプログラムが含まれていて、このプログラムがハードディスクにLinuxをインストールします。[4]

# ブータブルISOを手作りする

## 環境と必要なツール類

このサンプルはVirtualBox上にインストールしたCentOS 7.5の環境で実行しています。作成したISOの起動試験もVirtualBox上の仮想マシンイメージで行っています。

なお作業はすべてroot権限で行っています。

SELinuxを無効化します。

```
# setenforce 0
```

必要なパッケージをインストールします。

```
# yum install genisoimage squashfs-tools
```

## ルートファイルシステムの作成

最初に作業ディレクトリを掘ります。これ以降、特に明示していない場合はこの作業ディレクトリ内でコマンドを実行しています。

```
TMP="/var/make-disk/"
mkdir -p $TMP
cd $TMP
```

ルートファイルシステムとなるイメージを作成してEXT4でフォーマットします。

```
# dd if=/dev/zero of=rootfs.img bs=1M count=1200
# mkfs.ext4 -F rootfs.img
# file rootfs.img 
rootfs.img: Linux rev 1.0 ext4 filesystem data, UUID=b1fd3d60-a9f0-4684-a45d-44332478be76 (extents) (64bit) (large files) (huge files)
```

ルートイメージをマウントして sys, proc, dev ディレクトリをマウントします。

```
# mkdir -p ./rootdir
# mount -o loop,rw,sync rootfs.img rootdir
# mkdir ./rootdir/{dev,proc,sys}
# mount -o bind /dev ./rootdir/dev
# mount -o bind /proc ./rootdir/proc
# mount -o bind /sys ./rootdir/sys
```

yumコマンドでルートイメージ内にLinuxの実行環境をインストールします。

```
yum install -y --installroot=${TMP}/rootdir --releasever=7 kernel coreutils passwd sudo
```

ユーザー cos を作成して root を無効化します。
cosにはパスワードなしでsudoを実行する権限を与えます。(パスワードなしでrootログインできるようにしてもいいですが...)

```
# chroot rootdir
(in chroot)# useradd cos
(in chroot)# passwd -d cos
(in chroot)# visudo
---
#%wheel ALL=(ALL)       ALL
(...omit)
%wheel  ALL=(ALL)       NOPASSWD: ALL
---
(in chroot)# usermod -aG wheel cos
(in chroot)# usermod -L root
(in chroot)# exit
```

他にもキーボードやタイムゾーン等必要に応じて設定します。

これでルートファイルシステムは出来上がりです。この後カーネルと初期RAMディスクを取り出すのでイメージはマウントしたままにしておきます。

## ISOの作成

ディレクトリを掘ります。

```
# mkdir -p mkdir isoroot/{boot,isolinux}
```

SYSLINUXをダウンロードして、解凍したディレクトリにあるブートローダーisolinux.binと ldlinux.c32モジュールをコピーします。

```
# curl -LO https://mirrors.edge.kernel.org/pub/linux/utils/boot/syslinux/syslinux-6.03.tar.gz
# tar -xvzf syslinux-6.03.tar.gz
# cp -a syslinux-6.03/bios/core/isolinux.bin isoroot/isolinux/
# cp syslinux-6.03/bios/com32/elflink/ldlinux/ldlinux.c32 isoroot/isolinux/
```

isolinux.cfgを作成します。詳しい設定はSYSLINUXのWebページを参照してください。[5]

```
# vi isoroot/isolinux/isolinux.cfg
---
default basic0
label basic0
  kernel /boot/vmlinuz0
  append initrd=/boot/initrd0 root=live:CDLABEL=linux rootfstype=auto ro rd.live.image rhgb rd.luks=0 rd.md=0 rd.dm=0 
---
```

ルートファイルシステムにchrootしてISO用の初期RAMディスクを作成します。
既定で作成される初期RAMディスクには、ISOに含まれるルートファイルシステムをマウントするためのモジュールが含まれていないため、そのままでは使用できません。

```
# chroot ./rootdir
(in chroot)# dracut --xz --add "dmsquash-live convertfs pollcdrom" --omit plymouth --no-hostonly --no-early-microcode /boot/initrd0 `ls /lib/modules`
(in chroot)# exit
```

カーネルと初期RAMディスクをISOのディレクトリにコピーします。

```
# cp -a rootdir/boot/vmlinuz-3.10.0-862.9.1.el7.x86_64 ./isoroot/boot/vmlinuz0
# cp -a rootdir/boot/initrd0 ./isoroot/boot/initrd0
```

コピーしたらルートファイルシステムの/bootディレクトリにあるファイルは不要なので削除します。

```
# rm -rf rootdir/boot/*
```

rootファイルシステムイメージをアンマウントしてsquashfsで圧縮します。

```
# umount rootdir/sys
# umount rootdir/proc
# umount rootdir/dev
# umount rootdir
# mkdir -p squashfsroot/LiveOS
# mv rootfs.img squashfsroot/LiveOS/
# mksquashfs squashfsroot squashfs.img
```

作成したsquashfsイメージをISOのLiveOSディレクトリにコピーします。

```
# mkdir isoroot/LiveOS
# mv squashfs.img isoroot/LiveOS/
```

最終的にISOに含まれるファイルはこんな感じです。

```
./isoroot/
./isoroot/boot
./isoroot/boot/vmlinuz0
./isoroot/boot/initrd0
./isoroot/isolinux
./isoroot/isolinux/isolinux.bin
./isoroot/isolinux/ldlinux.c32
./isoroot/isolinux/isolinux.cfg
./isoroot/LiveOS
./isoroot/LiveOS/squashfs.img
```

最後にISOファイルを作成します。

```
# cd isoroot
# mkisofs -o ../cos7.iso -R -J -T -V linux -b isolinux/isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table .
```

使用しているmkisofsの主要なオプションは次のような感じです

- -o 出力ファイル名
- -R, -J, -T ISO9660標準ではサポートされないファイル名を扱うためのオプションです。
- -V _LABEL_ ISOのラベルを指定しています。上のサンプルでは "linux" を指定していますが、このディスクのラベルはisolinux.cfgに定義した"root=live:CDLABEL=linux"と対応させておく必要があります。
- -b El Toritoブートローダーであるisolinux.binを指定します。

作成したISOイメージを適当な仮想マシンにアタッチして起動してみてください。
手順に誤りがなければCentOS 7が起動してcosユーザーでログインできるはずです。

---

[1]https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/installation_guide/sect-disk-image-installation-automatic
[2]https://ja.wikipedia.org/wiki/ISO_9660
[3]https://www.syslinux.org/wiki/index.php?title=The_Syslinux_Project
[4]https://fedoraproject.org/wiki/Anaconda
[5]https://www.syslinux.org/wiki/index.php?title=Config