Windowsを維持したままHDDから少し小さいSSDにLinuxを使って引っ越した

# はじめに

親戚の使っているPCが重いので見てくれということでリソースモニタを見たところ
ディスクアクセス（HDD）が常に100%になっていたのでSSDに変えることにしました。

今回用意したSSDが元のHDDより少し小さいもの（1TB→960GB）となったため
ちょっとした追加手順が必要になります。

基本的には大抵の構成で使い回せる手順になるはずですが
新ディスクが元ディスクの半分以下の場合はできない可能性があります（パーティションの縮小章参照）
また、セクタサイズが512同士でない場合も動かないかもしれません（試してない）

また私のPCじゃないのでスクショはありません。
コマンドやログは再現したものになるので実際のものとは異なる可能性があります、ご注意ください。

なおqiita上の既存記事でもいくつかのWindows維持縮小移行記事がありますが、
・GPTである
・ディスク末尾にある回復パーティションを消さない
・縮小・拡大自体はWindows標準機能で行う（=Linuxやサードパーティツールでファイルシステムはいじらない）
といった点をすべて満たすものとしてはユニークかと思います。


# 用意するもの

・適当なSATA SSD
・適当なLive起動可能なLinux（転がっていたXubuntu 18.04でやりましたがもっと新しいものでいいはずです）
・適当なUSB-SATAアダプタ


# 現状の確認

Windowsのディスクの管理から確認
1TBのGPT、Cドライブのみ構成ですが実際はEFIと回復パーティションもあります。

```
 EFI     Cドライブ       回復
[ 1G |     927G     |   3G   ]
```

・・・ドライブのケツに別パーティションがあるのはいただけませんね（後述）
一旦再起動、セキュアブートを切り、Linuxを起動して確認

```
fdisk -l /dev/sda
Disk /dev/sda: 931.5 GiB, 1000204886016 bytes, 1953525168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
...
Device         Start        End    Sectors   Size Type
/dev/sda1       2048    2099199    2097152     1G EFI System
/dev/sda2    2099200    2361343     262144   128M Microsoft reserved
/dev/sda3    2361344 1947230207 1944868864 927.4G Microsoft basic data
/dev/sda4 1947230208 1953523711    6293504     3G Windows recovery environment
```

ディスクの管理ではreserved領域が表示されていないようです。


# パーティションの縮小

Windowsを起動し、ディスクの管理からCドライブを縮小します。

```
縮小前の合計サイズ(MB):     949643
縮小可能な領域のサイズ(MB): 430930
縮小する領域のサイズ(MB):   425355
縮小後の合計サイズ(MB):     524288
```

縮小後の合計サイズはなるべく小さくするとより早くコピーできますが
切りのいい数字(正確に512GiB)にしているのは趣味です。
なおドライブがどんなに空いていても初期サイズの約半分以下にはできない場合があるのでご注意ください。
なので1TBのHDD -> 480GBのSSDとかはこの手順では失敗する可能性があります。
はじめからドライブをCとDに2分割する等していればもうすこし自由度が高くなります
Dを外にコピーしてパーティション消し、Cを縮小すれば単純計算で75%縮小の余地があります。

終わったら再起動し、再びLinuxを起動します。

```
fdisk -l /dev/sda
...
Device         Start        End    Sectors  Size Type
/dev/sda1       2048    2099199    2097152    1G EFI System
/dev/sda2    2099200    2361343     262144  128M Microsoft reserved
/dev/sda3    2361344 1076103167 1073741824  512G Microsoft basic data
/dev/sda4 1947230208 1953523711    6293504    3G Windows recovery environment
```

sda3が適切に縮んでいることがわかります。


# 新SSDのエラーチェック

お好みで初期不良のチェックをします。
アダプタでSSDをつないで適当なコマンドで全領域を読み出します

```
$ hexdump -C /dev/sdb
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
```


# 新ディスクのパーティション設計

今回はfdiskを使用します。
fdiskにはsfdiskスクリプトファイルの読み書き機能があり、
より高度なパーティショニングが短い手順でできます。
（生のsfdiskコマンドもありますが使い勝手が微妙だったので今回はfdisk経由で使ってます）

```
  Script
   I   load disk layout from sfdisk script file
   O   dump disk layout to sfdisk script file
```

まず/dev/sdaのパーティションテーブルをエクスポートします。
`attrs="GUID:63"`のような謎の属性も引き継いでくれます。

```
fdisk /dev/sda
...
Command (m for help): O

Enter script file name: /dev/stdout <- 標準出力なりファイルなりお好みで
label: gpt
label-id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
device: /dev/sda
unit: sectors
first-lba: 34
last-lba: 1953525134

/dev/sda1 : start=        2048, size=     2097152, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, name="EFI system partition", attrs="GUID:63"
/dev/sda2 : start=     2099200, size=      262144, type=E3C9E316-0B5C-4DB8-817D-F92DF00215AE, uuid=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, name="Microsoft reserved partition", attrs="GUID:63"
/dev/sda3 : start=     2361344, size=  1073741824, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, uuid=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, name="Basic data partition"
/dev/sda4 : start=  1947230208, size=     6293504, type=DE94BBA4-06D1-4D40-A16A-BFD50179D6AC, uuid=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, name="Basic data partition", attrs="RequiredPartition GUID:63"
```

修正が必要となるため改めて新旧ディスクと見比べます。

```
fdisk -l /dev/sda
Disk /dev/sda: 931.5 GiB, 1000204886016 bytes, 1953525168 sectors
...

fdisk -l /dev/sdb
Disk /dev/sda: 894.3 GiB, 960197124096 bytes, 1953525168 sectors
...
```

まずlast-lbaが実際のディスクサイズの34手前(ディスク末の33手前)以降にありますので直します。
（無理やりGPTエントリ数をいじらない限りは34固定のはずです）
GPTはディスクの頭と末尾にパーティション情報を二重化して保存しているので
末尾まで使い切ることができず、ディスクサイズが完全に一致していない限り、
ディスクのフルコピーだけでは済みません。不便ですね。
（MBRなら小→大なら無条件に、大→小ならパーティションがディスク外に掛かっていなければコピーだけで良い）

https://ja.wikipedia.org/wiki/GUID%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB#/media/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:GUID_Partition_Table_Scheme.svg

次にメインとなるCドライブに相当するsda3の後にsda4があるため、開始位置をずらす必要があります。
ここが一番厄介で、セクタ位置を1MB境界に揃えるため適切な位置を算出する必要があります。
手前すぎると無駄な空き領域が発生し、後ろすぎるとパーティションが置けなくなります。

多分この式で良いと思います（無保証・現状ママ提供）

`(最終有効LBA + 1 - サイズ) / 2048 * 2048`

```
echo $(((1875384974+1-6293504)/2048*2048))
1869090816
```

省略しますが、パーティションが複数ある場合は更に面倒になります。
ドライブのケツに別パーティションがあるのはいただけませんね（二回目）。
あとはdevice名を変更し、まとめると以下のようになります。

```
label: gpt
label-id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
device: /dev/sdb
unit: sectors
first-lba: 34
last-lba: 1875384974

/dev/sda1 : start=        2048, size=     2097152, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, name="EFI system partition", attrs="GUID:63"
/dev/sda2 : start=     2099200, size=      262144, type=E3C9E316-0B5C-4DB8-817D-F92DF00215AE, uuid=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, name="Microsoft reserved partition", attrs="GUID:63"
/dev/sda3 : start=     2361344, size=  1073741824, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, uuid=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, name="Basic data partition"
/dev/sda4 : start=  1869090816, size=     6293504, type=DE94BBA4-06D1-4D40-A16A-BFD50179D6AC, uuid=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, name="Basic data partition", attrs="RequiredPartition GUID:63"
```

（・・・はい、パーティション名を直しそこねてますが動作しました、気になる場合は直したほうがいいかも）
あとはfdiskで新ディスクに流し込みます。


# 新ディスクにパーティションの作成

```
fdisk /dev/sdb
...
Command (m for help): I

Enter script file name: /dev/stdin <- 標準入力なりファイルなりお好みで
（標準入力なら貼り付けてCtrl+D）
...
Script successfully applied.

Command (m for help): p
...
Device         Start        End    Sectors  Size Type
/dev/sdb1       2048    2099199    2097152    1G EFI System
/dev/sdb2    2099200    2361343     262144  128M Microsoft reserved
/dev/sdb3    2361344 1076103167 1073741824  512G Microsoft basic data
/dev/sdb4 1869090816 1875384319    6293504    3G Windows recovery environment


Command (m for help): w
```


# パーティションのコピーと検証

あとはそれぞれのパーティションの中身をコピーしていきます。
statusやoflagはお好みで。
sda3が一番長いので最後にしてます

```
dd if=/dev/sda1 of=/dev/sdb1 bs=1M status=progress oflag=direct
（進捗は省略）
dd if=/dev/sda2 of=/dev/sdb2 bs=1M status=progress oflag=direct
dd if=/dev/sda4 of=/dev/sdb4 bs=1M status=progress oflag=direct
dd if=/dev/sda3 of=/dev/sdb3 bs=1M status=progress oflag=direct
```

終わったらハッシュを取り、一致していることを確認します。
sdaとsdbは端末並べて並列でやりました。もうこの時点でSSDのほうが速さが目立ってました。

```
（念のため、キャッシュを飛ばす）
echo 3 > /proc/sys/vm/drop_caches

md5sum /dev/sda1
md5sum /dev/sda2
md5sum /dev/sda4
md5sum /dev/sda3

md5sum /dev/sdb1
md5sum /dev/sdb2
md5sum /dev/sdb4
md5sum /dev/sdb3
```


# 後処理

筐体をバラしてディスクを差し替え、問題なく起動することを確認したら
ディスクの管理からCドライブを最大まで拡張したら一度止め、セキュアブートを戻したら完了です。


# 引き渡し

一晩使ってもらいましたが、目に見えて動作は改善したと好評だったのでよかったです。