ddに頼らずにLinuxのブートドライブをコピーする方法(LVMでもない)

# どんな話？

- 一度構築したLinux環境を、別のディスクに移行したい
 - HDD→SSD
 - 小さいSSD→大きいSSD
 - 他にもいろいろあるよね
- でもLVM組んでない
- というか /boot とか /boot/efi とかどうすんの
 - 特に /boot/efi はLVM組んでても避けられない
- 調べるとddを使う方法が出てくるけど…
 - ディスクのサイズが違う
 - GPartedとかで頑張ったけど失敗した
 - ↑Clonezillaなら多分できるけどそれでもうまく行かなかった

…という状況のときに、ddじゃなくてファイルコピーして頑張ろうという話。
この記事はFedora 30で実践して書いたけど、他のディストリビューションでも通用する…はず。
(Fedoraなら普通はLVMになるけど説明用です(´・ω・｀) )
…と思ってたら**ubuntuが想像以上に違っていてめっちゃつらい**。
ubuntuユーザーはうまいこと読み替えてほしい。


あとパーティションテーブルがGPTなのが前提。MBRだとパーティション操作が変わってしまうし、大抵BIOSブートなのでgrub2-installなどが必要になる。

# 概要
gdisk と mkfs と mount と rsync と /etc/fstab でがんばる。

# 手順

root権限が必要なコマンドは sudo をつけてある。
sudoを使わない環境では適宜読み替えること。

## 古いブートドライブの確認

### 古いブートドライブを探す

まず古いブートドライブはどこにいるのか確認する。
とりあえずdfコマンドを使う。

```bash
$ df
ファイルシス            1K-ブロック       使用     使用可 使用% マウント位置
# ブートドライブ以外は省略
/dev/sda1                    102182       7958      94224    8% /boot/efi
/dev/sda2                    491604     195493     266415   43% /boot
/dev/sda3                 136856160   13441568  116393024   11% /
```
環境によっては /boot や /boot/efi がないやつもある。
とりあえず、これでブートドライブが /dev/sda だとわかる。
(他の名前だった場合は以下の /dev/sda をそいつに読み替えること。)
ここで /dev/mapper/**** とか出てたら、そのパーティションはLVMだ。
LVMのコピーはこの記事では扱っていないので検索してほしい。その場合でも他のパーティションのコピーはこの記事の方法で可能である。


### 古いブートドライブのパーティションテーブルを確認する

次はブートドライブのパーティションテーブルを確認する。
gdiskを使う。もし「コマンドが見つかりません」とか出たらaptなりdnfなりでインストールする。

```bash
$ sudo gdisk /dev/sda
GPT fdisk (gdisk) version 1.0.4

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

Found valid GPT with protective MBR; using GPT.
```
"Found valid GPT with protective MBR; using GPT." と出ている。
すなわち「ちゃんとGPTパーティションがあるよ」と教えてくれている。
もし違う表示が出たら要注意。このまま続行しても失敗したり、二度と起動できなくなったりする可能性がある。

そしてパーティション一覧を出すには"p"という1文字のコマンドを打つ。
ちなみにヘルプは"?"で出る。

```bash
Command (? for help): p
Disk /dev/sda: 312581808 sectors, 149.0 GiB
Model: INTEL SSDSC2BB16
Sector size (logical/physical): 512/4096 bytes
Disk identifier (GUID): 7F41B9E9-BAE1-4F38-8C13-2CD6725320C3
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 312581774
Partitions will be aligned on 2-sector boundaries
Total free space is 1679 sectors (839.5 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          206847   100.0 MiB   EF00  EFI System Partition
   2          206848         1230847   500.0 MiB   8300  Linux filesystem
   3         1230848       312580095   148.5 GiB   8300  Linux filesystem
```
ここで必要なのは、最後に出ているパーティション一覧である。
これを丸ごとメモしておこう。
大抵のSSHクライアントならドラッグ等で選択からのコピペが効く。
実機を直接叩いてたりするなら携帯のカメラでも構わない。

なお、この例にはないが、Codeが"EF02"のパーティションがあった場合はコピー時の手順が少し変わる(後述)。
また、Codeが"8E00"のパーティションがある場合は、そいつはLVMの物理ボリュームだ。LVMのコピーはこの記事では扱っていないので検索してほしい。その場合でも他のパーティションのコピーはこの記事の方法で可能である。

## 新しいディスクの準備

### 新しいディスクを探す

まずは新しいディスクを探す必要がある。

```bash
$ sudo ls /dev/sd*
/dev/sda /dev/sda1 /dev/sda2 /dev/sda3 /dev/sdb
```
/dev/sdb にはパーティションが一つもないことがわかる。
というわけで"多分"これが新しいディスクである。
こんなディスクが複数あったり、そうでなくても不安な場合は、事故防止のためにも、PCの電源を切ってから、古いブートドライブと新しいブートドライブ以外を外して出直すのがよい。ただし、/etc/fstab で別のディスクをマウントしている場合は適宜コメントアウトで無効化などを行うこと。これを忘れると外したドライブをマウントしようとしてブートに失敗する。
もっとも、他のPCなどで使ったディスク等を使いまわす場合はこの方法では探せない(すでにパーティションがある！)ので総当たりが必要。

gdiskで本当にこれが新しいブートドライブ(にしたいディスク)かどうか確認する。

```bash
$ sudo gdisk /dev/sdb
GPT fdisk (gdisk) version 1.0.4

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

Creating new GPT entries in memory.
```
「パーティションテーブルがないから新しく作るよ」と教えてくれている。
パーティションテーブルを作る前に、まずは本当に目当ての新しいブートドライブかどうか確認する。

```bash
Command (? for help): p
Disk /dev/sdb: 3125627568 sectors, 1.5 TiB
Model: VK1600GECVP
Sector size (logical/physical): 512/4096 bytes
Disk identifier (GUID): 4C86D095-3737-4895-93CC-DFB9196FFB46
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 3125627534
Partitions will be aligned on 2048-sector boundaries
Total free space is 3125627501 sectors (1.5 TiB)

Number  Start (sector)    End (sector)  Size       Code  Name
```
Model(ディスクの型番)やディスク容量などを見て確認する。
逆に言えばこれくらいしか手掛かりがないので、事故防止のためにもブートドライブ交換と関係ないディスクは外しておくことを勧める。

### パーティションテーブルを作成する

これだ、と確認したらパーティションを作っていく。
先にメモしたパーティション一覧を使う。
すでにパーティションがある場合は"d"コマンドで消してから行う。

パーティション1個を作る操作はこのようになる。

```bash
Command (? for help): n
Partition number (1-128, default 1): #何も入れずにEnter
First sector (34-3125627534, default = 2048) or {+-}size{KMGTP}: #何も入れずにEnter
Last sector (34-3125627534, default = 3125627534) or {+-}size{KMGTP}: +100M
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300): EF00
Changed type of partition to 'EFI System'
```
- "Last sector"の行でパーティションの大きさを決められる
 -  "+100M" と入れると100MBのパーティションになる
 -  "+100G" なども使える
 -  /boot と /boot/efi については古いブートドライブに合わせるとよい
 -  何も入れずにEnterを押すとディスクの終わりまで全部使う。"/"(ルートパーティション)は最後にこれで作るとよい 
- "Hex code or GUID"の行でパーティションの種類を指定する
 - これは必ず古いブートドライブの各パーティションの"Code"と同じにする

基本的には古いブートドライブと同じ順番でパーティションを作っていく。順番を変えると事故の元なのでおすすめしない。
全部入れるとこのようになる。
この例では /boot/efi と /boot 用のパーティションは古いブートドライブのものと同じ大きさとし、残り全部を /(ルートパーティション) に割り当てた。

```bash
Command (? for help): p

# ディスク情報は省略

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          206847   100.0 MiB   EF00  EFI System Partition
   2          206848         1230847   500.0 MiB   8300  Linux filesystem
   3         1230848      3125627534   1.5 TiB     8300  Linux filesystem
```
最後に"w"コマンドでパーティションテーブルを書き込む。
これは取り消せないのでよく確認すること。
逆に言えばこれをやる前ならやり直しが効く。("q" コマンドでgdiskを終了など)

```bash
Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/sdb.
The operation has completed successfully.
```

### 新しいブートドライブをフォーマットする

まず、新しく作ったパーティションをLinuxが認識しているかどうか確認する。

```bash
$ sudo ls /dev/sd*
/dev/sda /dev/sda1 /dev/sda2 /dev/sda3 /dev/sdb /dev/sdb1 /dev/sdb2 /dev/sdb3
```
この例では /dev/sdb[1-3] が増えているのでOKだ。
もし増えていない場合は再起動が必要だが、以下の注意が必要。

- 空のEFIシステムパーティションを作ってしまったのでブートに失敗する可能性がある
 - ブートデバイスを手動で選択すればブートできるはず
- 再起動後に /dev/sd[abc…] が入れ替わっている可能性がある
 - **必ず「古いブートデバイスを探す」をやり直してよく確認すること！**

無事に出来たらフォーマットをしていく。
dfコマンドで古いブートドライブの各パーティションのファイルシステムを確認する。

```bash
$ df -T
ファイルシス            タイプ   1K-ブロック       使用     使用可 使用% マウント位置
# ブートドライブ以外は省略
/dev/sda1               vfat          102182       7958      94224    8% /boot/efi
/dev/sda2               ext4          491604     195493     266415   43% /boot
/dev/sda3               ext4       136856160   13442692  116391900   11% /
```
この「タイプ」に合わせて、新しいブートドライブの各パーティションをフォーマットしていく。
基本的には"mkfs.xxx" に「タイプ」を当てはめればそのファイルシステムでフォーマットできる。
**間違って古いブートドライブをフォーマットしないように注意。** もっとも、警告が出てできないはずだが。

```bash
$ sudo mkfs.vfat /dev/sdb1
# 出力は省略
$ sudo mkfs.ext4 /dev/sdb2
# 出力は省略
$ sudo mkfs.ext4 /dev/sdb3
# 出力は省略
```

## いよいよファイルコピー

これからの作業は、使用しているLinuxディストリビューションのインストールディスクなりLive USBメモリなりを用意して、そこからブートして行う必要がある。
使っているディストリビューションにレスキューモードが用意されていれば、それを使う手もある。

### 新旧ブートドライブを探す

まずはディスクの一覧を見る。

```bash
$ sudo ls /dev/sd*
/dev/sda /dev/sda1 /dev/sda2 /dev/sda3 /dev/sdb /dev/sdb1 /dev/sdb2 /dev/sdb3
/dev/sdc /dev/sdc1
```
そして /dev/sd[abc…] それぞれをgdiskで表示して新旧ブートドライブを特定する。

```bash
$ sudo gdisk /dev/sda
GPT fdisk (gdisk) version 1.0.4

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

Found valid GPT with protective MBR; using GPT.
Command (? for help): p
Disk /dev/sda: 312581808 sectors, 149.0 GiB
Model: INTEL SSDSC2BB16
Sector size (logical/physical): 512/4096 bytes
Disk identifier (GUID): 7F41B9E9-BAE1-4F38-8C13-2CD6725320C3
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 312581774
Partitions will be aligned on 2-sector boundaries
Total free space is 1679 sectors (839.5 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          206847   100.0 MiB   EF00  EFI System Partition
   2          206848         1230847   500.0 MiB   8300  Linux filesystem
   3         1230848       312580095   148.5 GiB   8300  Linux filesystem
```
この例では /dev/sda が古いブートドライブだ。
同様に残りの /dev/sd[bc…] も確認する。

以下は

- 古いブートドライブ: /dev/sda
- 新しいブートドライブ : /dev/sdb

だったとして記載する。前の作業とは名前が変わっていることも多いので要注意。

### 各パーティションをマウントしてコピーする

特定した各パーティションをマウントしてコピーする
なお、**Codeが"EF02"のパーティションはこの方法ではコピーできない。**後述の「Codeが"EF02"のパーティションをコピーする」を参照。

```bash
# 手動マウントはどこでもできるけど /mnt にフォルダを作るのがお約束
$ cd /mnt

# 古いブートドライブの /boot/efi をマウント
# フォルダ名はなんでもいいけど事故防止のためわかりやすくしよう
$ sudo mkdir efi_old
$ sudo mount /dev/sda1 efi_old
# 一応中身を見ておく(なんかあるはず)
$ ls efi_old

# 新しいブートドライブの /boot/efi をマウント
$ sudo mkdir efi_new
$ sudo mount /dev/sdb1 efi_new
# こっちは空っぽのはず
$ ls efi_new

# コピーするよ！
# efi_old の「中身を」efi_newにコピー
$ sudo rsync -aHAXxSvP --numeric-ids efi_old/ efi_new/
```
rsyncのオプションはググれば出てくるが、概ね「ファイルシステムをそっくりそのままコピーし(aHAXxS --numeric-ids)、進捗を表示する(vP)」という意味。
オプションの文字の順番は問われないが、大小文字は区別されるので間違えないように。
また"efi_old/"の最後の"/"を付け忘れると、efi_new の中に efi_old というフォルダが作られてその中にコピーされてしまうので必ずつける必要がある。

残りのパーティションも同様にコピーする。

```bash
# /boot をコピー
$ sudo mkdir boot_old
$ sudo mount /dev/sda2 boot_old
# 一応中身を見ておく(なんかあるはず)
$ ls boot_old

$ sudo mkdir boot_new
$ sudo mount /dev/sdb2 boot_new
# こっちは空っぽのはず
$ ls boot_new

$ sudo rsync -aHAXxSvP --numeric-ids boot_old/ boot_new/

# / (ルートパーティション)をコピー
$ sudo mkdir root_old
$ sudo mount /dev/sda3 root_old
# 一応中身を見ておく(なんかあるはず)
$ ls root_old

$ sudo mkdir root_new
$ sudo mount /dev/sdb3 root_new
# こっちは空っぽのはず
$ ls root_new

$ sudo rsync -aHAXxSvP --numeric-ids root_old/ root_new/
```

### Codeが"EF02"のパーティションをコピーする(あった場合のみ)
Ubuntuをデフォルトの設定でインストールするとあったりする。これもブートに必要なのでコピーする。
このパーティションはddでコピーする必要がある。

```bash
# 古いブートドライブの"EF02"なパーティションが /dev/sda1 で
# 新しいブートドライブの"EF02"なパーティションが /dev/sdb1 の場合の例
# もちろん自分の環境に合わせて読み替えること

$ sudo dd if=/dev/sda1 of=/dev/sdb1
```

さらにGRUBを新しいブートドライブにインストールする必要がある。
このコマンドはディストリビューションによって異なる。例えばUbuntu 18.04ではgrub-install("2"がない)だったりする。

```bash
# 新しいブートドライブが /dev/sdb の場合
$ sudo grub2-install /dev/sdb
```


### 「新しいブートドライブの」/etc/fstab を編集する

ddでディスクコピーをした場合とは異なり、/etc/fstabを編集しないとブートに失敗する。
「新しいブートドライブの」 /etc/fstab を編集する必要がある。

その前に各パーティションのUUIDをメモしておく。blkidコマンドを使う。
なお、"/"(ルートパーティション) の "PARTUUID" もメモしておく。後で使う。

```bash
$ sudo blkid
# 新しいブートドライブ以外は省略
/dev/sdb1: SEC_TYPE="msdos" UUID="2FED-0CE8" TYPE="vfat" PARTLABEL="EFI System Partition" PARTUUID="24dfff22-b893-469d-a94c-dde0d53522bc"
/dev/sdb2: UUID="c1b31131-99e7-41ee-86e2-a2e3e5b46033" TYPE="ext4" PARTLABEL="Linux Filesystem" PARTUUID="7aaf4b56-9de3-464e-87f6-f13a2c0b7eb7"
/dev/sdb3: UUID="7d255831-7a7e-4c39-9c27-a51190b4b12e" TYPE="ext4" PARTLABEL="Linux Filesystem" PARTUUID="b04a09cf-e64e-4385-b74c-d8b4231611f1"
```

先の例では /mnt/root_new に新しいブートドライブのルートパーティションをマウントしたので
/mnt/root_new/etc/fstab を編集すればいい。

```bash
# お好みのテキストエディタで /mnt/root_new/etc/fstab を開く
# nanoとかviとか 例ではviだけど
$ vi /mnt/root_new/etc/fstab
```
各行は先頭から file system(マウントするパーティション), mount point, type…と並んでいる(詳しくはググって)。
ここでは先頭の"file system"だけ書き換えればよい。
この部分を、blkidコマンドの"UUID"の値に書き換える。
"PARTUUID"を使っても問題なく動くが、どういうわけかLinuxインストーラーはUUIDを使いたがるのでこの例ではそれに倣っておく。

書き換えが終わるとこんな感じになる。

```bash:/etc/fstab
# ブートドライブ以外は省略
UUID="7d255831-7a7e-4c39-9c27-a51190b4b12e" /                       ext4    defaults        1 1
UUID="c1b31131-99e7-41ee-86e2-a2e3e5b46033" /boot                   ext4    defaults        1 2
UUID="2FED-0CE8"                            /boot/efi               vfat    umask=0077,shortname=winnt 0 2
```

## 新しいブートドライブで起動

PCの電源を切ってから、古いブートドライブやコピー時の起動に使ったUSBメモリを外す。
ただし、GRUBはまだ新しい "/"(ルートパーティション)を知らないので、そのままだとブート途中で止まってしまう。

### GRUBで新しいルートパーティションを指定する
GRUBの画面が出ているときに"E"キーを押して、起動コマンドを編集する。
起動時にそれっぽい画面が出ないようなら、BIOSっぽい画面からずっとShiftキーを押しっぱなしにしてると出る…らしい。
起動コマンドからこんな感じの行を探して編集する。

```bash
linux    /boot/vmlinuz-5.0.17-300.fc30.x86_64 root=PARTUUID=b04a09cf-e64e-4385-b74c-d8b4231611f1 console=tty1
```
先にblkidコマンドで確認した "/"(ルートパーティション)のPARTUUIDを、このように root=*** の部分に入れてあげればいい。
/etc/fstabではUUIDを""で囲っていたが、ここのPARTUUIDは囲わない。
最初から UUID=**** などとなっていたらUUIDを入れれば多分動く。
また、GRUBは日本語キーボードに対応していない。日本語キーボードで"="を入力するには、その右隣にある"^"キーを押す必要がある。

編集が終わったら、Ctrl+X を押してブートを開始させる。

### GRUBの設定を更新する

無事に新しいブートドライブから起動したら、最後にGRUBに新しいルートパーティションを覚えさせる。
これはディストリビューションによってコマンドが異なるので調べること。例はFedora30の場合である。
Ubuntuなら```grub-mkconfig```("2"がない)になる。

```bash
$ sudo grub2-mkconfig -o /etc/grub2-efi.cfg
```

### 後処理とか失敗したときとか

/etc/fstabで他のディスクをマウントするようにしてあった場合は再び有効にしよう。

起動に失敗した場合は、再びLive USBメモリ等から起動して、新しいブートドライブの中身を確認しよう。
(gdiskでパーティションテーブルを確認したり、各パーティションを mount して中身を確認したりする。)