2021/01/30

ディスクイメージにGrub2をインストールする

ディレクトリをQEMUのディスクイメージに変換する作業において、ディスクイメージにブートローダーをインストールする必要があったのでやってみた。

まずは、必要なサイズのRAWイメージを作成する。とりあえず、4GBで(1M x 4000)。

# dd if=/dev/null of=raw_disk.img bs=1M seek=4000
0+0 records in
0+0 records out
0 bytes copied, 0.000167545 s, 0.0 kB/s

余談:

dd if=/dev/zero of=raw_disk.img count=1 bs=1000MB
というやり方もあるが、実際にディスクにデータを書き込んでいくので時間がかかる。

ちなみに、上記のseekオプションは、seekで指定された回数だけブロック(サイズ指定はbs)のコピーをスキップするという動作をするので、上記の例では、1MBのブロックのコピーを4000回スキップした後に、/dev/nullから1回だけ1MBをコピーするという動作をする。(でも、/dev/nullは空なので、0バイトがコピーされることになる。)

スキップされたところはスパースファイル(Sparse file)になっているので、使用されるディスク容量を節約できるし、ddでファイルを作成するスピードを大幅に短縮できる。

作成したRAWイメージをデバイスとして認識させる。--findは/dev/loop[0-9]のうち、空いている番号を見つけるという意味。--showは、選ばれたデバイスパスを表示するという意味。

# losetup --find --show raw_disk.img
/dev/loop0

注意:ここでループバックデバイスとしてマウントしているが、

# mount -t ext4 raw_disk.img /mnt/raw_disk/

という方法もある。しかし、この方法でマウントして後続処理を行っていくと、grub2-installをする際にブートローダーをインストールする先を指定できない(/dev/ で始まるブロックデバイスを指定する必要があるが、存在しないため。

作成したドライブをフォーマットする。この例は、RAWイメージのファイル自体を初期化する例。

# mkfs.ext4 -F raw_disk.img
mke2fs 1.45.6 (20-Mar-2020)
Discarding device blocks: done
Creating filesystem with 1024000 4k blocks and 256000 inodes
Filesystem UUID: 07f46161-9dba-49dd-aad5-0f871d1689d4
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

こちらの例は、先程作成したループバックデバイス経由で初期化する例。

# mkfs.ext4 /dev/loop0
mke2fs 1.45.6 (20-Mar-2020)
Discarding device blocks: done
Creating filesystem with 1024000 4k blocks and 256000 inodes
Filesystem UUID: 0c3c3114-74d4-4012-b131-f1dd67d4a4e0
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

作成したディスクイメージのマウントと、ディスクイメージへのファイルの書き込み。ここでは、USBブートできるように構築されたイメージ(squashfsに入っていたものを取り出した)を展開したディレクトリの中身をコピーした。

# mkdir /mnt/ram_disk
# mount /dev/loop0 /mnt/raw_disk/
# cp -a squashfs-root/. /mnt/raw_disk/


マウントしたディレクトリを/にしてchrootする。

# cd /mnt/raw_disk/
# mount --bind /dev/ dev/
# chroot .
chrooted:/# mount -t proc none /proc
chrooted:/# mount -t sysfs none /sys
chrooted:/# mount -t devpts none /dev/pts

 

Grub2をインストールする。起動時のbootディレクトリは/bootになるはずなので、--boot-directoryで指定。ブートローダのインストール先はlosetupで作成した/dev/loop0

chrooted:/# grub-install --boot-directory=/boot /dev/loop0
Installing for i386-pc platform.
grub-install: warning: File system `ext2' doesn't support embedding.
grub-install: warning: Embedding is not possible.  GRUB can only be installed in this setup by using blocklists.  However, blocklists are UNRELIABLE and their use is discouraged..
grub-install: error: will not proceed with blocklists.

しかし、エラーになる。

これは、インストール先が数字で終わっているため。本来、ブートローダはデバイスの先頭に書き込まれるものであり、例えば/dev/sdaなどを指定する。パーティション(例:/dev/sda0など)の先頭に書くのはあまりないはずであるため、デバイスの指定の末尾が数字になることは通常はありえないため、エラーになっている。しかし、今回やろうとしているのは、末尾が数字になっていても、後々はディスクの先頭になる部分であるため、このエラーは無視して良い。そこで、--forceを指定して、強制的に書き込む。

chrooted:/# grub-install --boot-directory=/boot --force /dev/loop0
Installing for i386-pc platform.
grub-install: warning: File system `ext2' doesn't support embedding.
grub-install: warning: Embedding is not possible.  GRUB can only be installed in this setup by using blocklists.  However, blocklists are UNRELIABLE and their use is discouraged..
Installation finished. No error reported.


/boot/grub/grub.cfgがあればよいのだが、ない場合は作成しなければならない。

chrooted:/# grub-mkconfig -o /boot/grub/grub.cfg
Generating grub configuration file ...
/usr/sbin/grub-probe: error: failed to get canonical path of `/data/hagiwara_usb/extracted_files/raw_disk.img'.
No path or device is specified.
Usage: grub-probe [OPTION...] [OPTION]... [PATH|DEVICE]
Try 'grub-probe --help' or 'grub-probe --usage' for more information.

が、エラー。grub-mkconfigが内部で呼び出しているgrub-probeがエラーを出している。いろいろ調べたが、最終的に別端末から/usr/share/grub/grub-mkconfig_lib をコピーして持ってきてエラーを回避した。(何故か、prepare_grub_to_access_device 関数の中の/dev/loop0の処理において、$@をshiftしていた。これにより、後続のgrub-probeの実行時に、--device の引数が無くなってしまい、エラーになっていた。)

再度grub-mkconfigを実行

chrooted:/etc/grub.d# grub-mkconfig -o /boot/grub/grub.cfg
Generating grub configuration file ...
Warning: Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported.
done

これで起動させたが、grubの対話モードで停止していた。grub.cfgを見るとkernelが見つけられていなかった。

/etc/grub.d/10-linux の中でkernelを見つけているのだが、冒頭で/dev/loop0についての処理をしており、ループバックデバイスのときはexitすることになっていた。

これをコメントアウトして、ループバックデバイスでも処理が継続するように変更。変更点はこんな感じ。なお、ループバックデバイスの元ファイルを特定して、そこを見せようとしているので、それも修正。

chrooted:/etc/grub.d# diff -Naur 10_linux.orig 10_linux
--- 10_linux.orig       2020-11-17 20:32:17.000000000 +0900
+++ 10_linux    2021-01-30 20:56:41.093564059 +0900
@@ -52,12 +52,12 @@
 # the initrds that Linux uses don't like that.
 case ${GRUB_DEVICE} in
   /dev/loop/*|/dev/loop[0-9])
-    GRUB_DEVICE=`losetup ${GRUB_DEVICE} | sed -e "s/^[^(]*(\([^)]\+\)).*/\1/"`
+    #GRUB_DEVICE=`losetup ${GRUB_DEVICE} | sed -e "s/^[^(]*(\([^)]\+\)).*/\1/"`
     # We can't cope with devices loop-mounted from files here.
-    case ${GRUB_DEVICE} in
-      /dev/*) ;;
-      *) exit 0 ;;
-    esac
+    #case ${GRUB_DEVICE} in
+    #  /dev/*) ;;
+    #  *) exit 0 ;;
+    #esac
   ;;
 esac

GRUB_DEVICE=の行をコメントアウトしていなかったときは、これまたgrub-probeでエラーが出ていた。

chrooted:/etc/grub.d# grub-mkconfig -o /boot/grub/grub.cfg
Generating grub configuration file ...
Warning: Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported.
/usr/sbin/grub-probe: error: cannot find a GRUB drive for /data/raw_disk.img.  Check your device.map.
Found linux image: /boot/vmlinuz-4.15.0-133-generic
Found initrd image: /boot/initrd.img-4.15.0-133-generic
done

いずれにせよ、grub-mkconfigで/boot/grub/grub.cfgを作成する。

chrooted:/etc/grub.d# grub-mkconfig -o /boot/grub/grub.cfg
Generating grub configuration file ...
Warning: Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported.
/etc/grub.d/10_linux: 62: /etc/grub.d/10_linux: Syntax error: newline unexpected (expecting ")")
root@edo:/etc/grub.d# grub-mkconfig -o /boot/grub/grub.cfg
Generating grub configuration file ...
Warning: Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported.
Found linux image: /boot/vmlinuz-4.15.0-133-generic
Found initrd image: /boot/initrd.img-4.15.0-133-generic
done

終わりのコメント

あとは後処理。chootの終了、マウントの解除、ループバックデバイスの解除。

chrooted::/# umount /proc || umount -lf /proc
chrooted::/# umount /sys
chrooted::/# umount /dev/pts
chrooted::/# umount /dev
chrooted::/# exit
# cd ..
# umount /mnt/raw_disk
# losetup -d /dev/loop0

以上で終わり。

0 件のコメント:

コメントを投稿