虎之助の徒然記

クローンディスクの作り方 (1) Linuxの場合

【概要】
 Linuxを用いたクローンディスクの作り方について説明します。ディスククローンには、dd、gdisk、gpartedの3つのコマンドを用います。それぞれの使い方や注意事項についてまとめます。

1. はじめに

 容量拡大のためにディスクのクローンを作成したり、SSD換装のために容量が小さなクローンディスクを作成したい場合があります。本稿では、これらをLinuxを用いて実現する方法について説明します。

2. クローンディスクの作成

 クローンディスクの作成には、主に次の3つのケースがあります。

  • ミラーリング:ディスクパーティションの大きさを変更せずにコピーする。
  • 拡張:サイズが大きなディスクに移し替える。
  • 縮小:サイズが小さなディスクに移し替える。

 それぞれのケースのクローンの作成について説明します。

 記事では、次のデバイス名を使っています。

  • /dev/sdS:オリジナルのディスク
  • /dev/sdT:クローン化するターゲットディスク
  • /dev/sdW:作業用のディスク

 マシン環境に合わせて、適宜、/dev/sdbや/dev/sdcと読み替えてください。

 なお、この記事は、パーティションテーブルは GPT (GUID Partion Table) で作成されていることを前提としてます。

3. 事前準備

3.1 必要なツールのインストール

3.1.1 dd、gdisk、gparted、ntfs-3gなど

$ apt install gdisk gparted ntfs-3g coreutils

 実際に、検証を行った Linux は Ubuntu18.04 です。aptを使う Debian 系のOSであれば、ほとんど同じパッケージ名でインストールできると思います。

 また、dd や shred が入っている coreutils はインストール済のはずですので通常は指定不要です。gdisk、ntfs-3g、gpartedなども、他のパッケージと同時にインストールされる場合があります。

3.1.2 gnome-disks

 必須ではありませんが、gnome-disk-utilityパッケージの中の gnome-disks もパーティションや SMART の情報などを見ることができて、ディスク管理に便利なコマンドです。

$ apt install gnome-disk-utility

3.1.3 parted

 partedでもパーティションテーブルを編集できますが、基本的には用いません。但し、パーティションテーブルの種類を調べるには、gdisk よりも parted の方が分かりやすいです。

$ apt install parted

 例えば、次のオプションでパーティションの種類を確認できます。

$ parted -l

あるいは

$ fdisk -l

「Partition Table」が「gpt」であれば GPT(GUIDパーティションテーブル)、「msdos」であれば MBR(マスターブートレコード)です。

 Windowsの場合、「ディスクの管理」 からディスクのプロパティを開き、「ボリューム」タブの「パーティションのスタイル」が「GUIDパーティションテーブル(GPT)」であることを確認します。MBRの場合には「マスターブートレコード(MBR)」と表示されます。

3.1.4 sudo について

 パッケージのインストールはroot権限が必要なので、sudo apt .... と sudo をつけて実行するか、rootにログインして実行します。ubuntu 系のOSではデフォールトではrootにはログインできないようになっているので、sudo を使うことが一般的です。

3.2 Windowsのシャットダウン

  • Windowsのディスクは、完全シャットダウンを行うこと*1

 完全シャットダウンをしていないntfsパーティションをLinuxではリサイズを行うことはできません。マウントもリードオンリーです。

 Windowsシステムが入ったディスクでも、外付けディスクとして別のWindowsマシンに接続して、完全シャットダウンしても大丈夫です。

3.3 ディスクの初期化

 今回の方法は、簡単な方法ですが、ある意味、乱暴な方法です。なぜなら、ディスクの末尾のパーティションテーブルを正しく取り扱っていません。

 ディスクに残った以前のGPTテーブルが悪さをするかもしれません。このため、ターゲットディスク・作業用ディスクを完全に初期化しておいた方が安心です(たぶん、不必要とは思いますが、ディスクを使いまわすときの習慣でいつもshredしているので、念のため書いておきます)。

$ shred -v -z -n 0 /dev/sdT /dev/sdW

 このコマンドで、ディスクの全セクタを0値に初期化します(非常に時間がかかります。2TBで丸一日ぐらい)。

3.4 本稿検証のためのデータの作成

 本稿で用いた方法は、loopデバイスを用いて検証しています。この検証のためのデータは、次のように作成しています。

  • 予め0値で埋まったファイルを作成し、loopデバイスと紐づけする。
     loopデバイスの使い方は、以前の記事
  • ファイルサイズは、以下の通り(1 MiB=1024x1024 bytes)。
    • sdS, sdW:100MiB
    • sdT1:80MiB
    • sdT2:100MiB
    • sdT3:120MiB
  • sdSのファイルシステムとパーティションサイズ:
    • part1:FAT16(30MiB)
      • FAT16としたのは、容量が小さすぎて、FAT32では作成できなかったため。
    • part2:NTFS(30MiB)
    • part3:ext4(30MiB)
    • 未割り当て(10MiB)
  • sdSに収めるデータ。
     ファイルがある領域とない領域が交互に現れるようにファイルを作成。
    • ① 未使用領域と同じサイズの乱数を書き込んだファイルを作成、直ぐに削除する。
      $ dd if=/dev/zero of=tmpfile ; shred -v -n 1 tmpfile ; rm tmpfile
      
    • ② 異なる乱数の1MiBのファイルで、ファイルシステムを満たす。
    • ③ 1, 3, 5,..., 17, 19番目の 10個のファイルの(10MiB)を残し、それ以外は削除する。

4. ミラーリングする場合

 パーティションサイズを変更せず、そのままの構成を保つ場合です。

4.1 同一サイズのディスクへコピーする場合

 オリジナルのディスクと全く同一のセクター数を持つディスクを使う場合です。

① ターゲットディスクへのコピー

 ddコマンドを用いてディスクをコピーします。これで終了です。

$ dd if=/dev/sdS of=/dev/sdT

 但し、例えば、同じ1TBのディスクといっても、セクタ数が同じとは限りません(違うと思っていた方がよいです)。

 同じブランド、同じ型番の商品を同時期に購入すれば、一致する可能性は高いですが、それでも、異なることがあるかもしれません。

4.2 大きなディスクへコピーする場合

 オリジナルのディスクよりも大きいセクター数を持つディスクへコピーする場合です。余分なセクタは使用せず、未割り当てのままとします。

① ターゲットディスクへのコピー

$ dd if=/dev/sdS of=/dev/sdT

② ターゲットディスクのGPTテーブルの修正

 GPTテーブルでは、第2GPTテーブル(バックアップGPTテーブルともいう。内容は第1GPTテーブルと同一)がディスク末尾のセクタに配置されなければなりません。そのため、ddでコピーしたそのままの状態では ターゲットディスク は、GPTテーブルの仕様に準拠しません。

● この状態で、gnome-disksでディスクを見ると、余分なセクタ部分が見えない状態となっています。余分なセクタ部分を未割り当てのままとして使用しないのなら、修正する必要はないのかもしれません。
● gpartedでは、GPTテーブルの位置不正を自動的に検知します。また、修正することも可能です。

 この問題を修正するために、gdiskを用いて、次のように第2GPTテーブルをディスク末尾に移動させます。

$ gdisk /dev/sdT 
Command (? for help): b              (GPTテーブルのバックアップを作成)
Enter backup filename to save: gpt-backup.dat 
Command (? for help): x              (Expertメニューに入る)
Expert command (? for help):  e      (第2GPTテーブルをディスク末尾に移動)
Expert command (? for help):  w      (変更したテーブルをディスクに書き込む)
$ partprobe /dev/sdT

 gdsikでは、GPTテーブルを新規に作り直したり、パーティションを作成・削除できます。しかし、これらの操作を行ってはなりません。なぜなら、これらの操作は、ディスクやパーティションにつけられたGUID/UUIDを変更してしまうからです。

 GUID/UUIDを変更すると、ディスク内のシステムがGUID/UUIDを参照していれば、問題が発生する可能性があります。例えば、Linuxでは、 /etc/fstab のディスクマウント定義でUUIDを用いることがあります。この場合、UUIDを変更すれば、対象のパーティションはマウントされなくなります。

 また、起動に関連するファイルシステムは、リサイズ・移動をしない方がよいでしょう。Linuxの場合、grubが起動イメージの配置をセクタで管理しています。パーティションをリサイズ・移動した場合には、起動イメージの配置されるセクタが変更される可能性があります。この場合、(LiveUSBなどを使って)grub コンフィギュレーションを修正します。

gdiskの使用上の注意事項

  • 作業の最初に、必ず、GPTテーブルのバックアップを取ること。
  • こまめに "p"(テーブルの表示)、"v"(検証)を行い、状況を把握すること。
  • オリジナルディスクとターゲットディスクのパーティションの一致を確認すること。
  • コマンドw(GPTテーブルをディスクへ書込んで終了)は、慎重に。
  • パーティション変更後は、partprobeを実行すること。

5. ディスクを拡張する場合

 サイズが大きなディスクに移し替える場合です。この場合、GPTテーブルの修正に加えて、パーティションの移動・拡大を行う必要があります。

① ターゲットディスクへのコピー

$ dd if=/dev/sdS of=/dev/sdT

② ターゲットディスクのGPTテーブルの修正

 ミラーリングの場合と同様に、gdiskで第2GPTテーブルをディスク末尾に配置します。

$ gdisk /dev/sdT 
Command (? for help): x              (Expertメニューに入る)
Expert command (? for help):  e      (第2GPTテーブルをディスク末尾に移動)
Expert command (? for help):  w      (変更したテーブルをディスクに書き込む)
$ partprobe /dev/sdT

③ ターゲットディスクのパーティションの拡大

 gpartedを用いて、パーティションの拡大を行います。

 今回は、それぞれ30MiBのfat16、ntfs、ext4のパーティションサイズを10MiB増加させて、40MiBにします。

  • gpartedを起動する
$ gparted /dev/sdT 
f:id:toranosuke_blog:20180912175236p:plain
当初のパーティション構成。ターゲットのデバイス名は/dev/loop1。

  • ディスクの末尾側のパーティション(/dev/loop1p3)をリサイズ・移動を指定する。

 「パーティション(P)」→「リサイズ/移動(R)」を選択し、前後の空き領域やサイズを指定し、「リサイズ/移動」をクリックする。

f:id:toranosuke_blog:20180912181845p:plain
前後の空き領域・サイズを指定する。

f:id:toranosuke_blog:20180912182253p:plain:w400
ブートに失敗するかもね、と警告される。

f:id:toranosuke_blog:20180912182807p:plain

 前方に19MB、後方に1MBの空き領域ができ、サイズも19MBとなる。これは、2048セクタ(=2048x512=1MiB)単位のアライメント調整を行っているため *2。リサイズパラメータの指定画面で「位置合わせ」を「なし」とすると、後方の空き領域をなくすことができる。

  • 同様に、ntfs、fat16の領域もリサイズ・移動の指定を行う。
f:id:toranosuke_blog:20180912184231p:plain

 パーティション間に隙間ができてしまった。ちゃんとリサイズ・アライメントすれば、パーティション間には隙間はできないはずだが、なぜか今回は隙間ができた。

f:id:toranosuke_blog:20180912190535p:plain
アライメント調整をなしにして、隙間を埋めた。

  • リサイズ・移動を実行する。

 「編集(E)」→「保留中の全ての操作を適用する(A)」をクリックして、リサイズ・縮小処理を実行します。

  今回の設定での検証実験では、ext4のパーティションの拡大に失敗し、エラーが発生しています。

f:id:toranosuke_blog:20180912203929p:plain:w300
エラーを知らせるポップアップ。

f:id:toranosuke_blog:20180912203935p:plain:w400
末尾のパーティションext4の拡大でエラーが発生。

 末尾のパーティション(ext4)の拡大に失敗していますが、それ以外は問題ありません。この失敗も、パーティションの拡大がディスク末尾の第2GPTテーブルを上書きする不正な要求だったためです。エラー判定されますが、チェック段階で不正と判定されているため、最後の処理は実行されていません。
  • 拡大後のパーティション構成

 最初のパーティション(fat16)と最後のパーティション(ext4)が1MiB足りず、計2MiB足りませんが、それぞれ、第1GPTテーブル、第2GPTテーブルが配置されている領域にはパーティションが拡張できないためです。

f:id:toranosuke_blog:20180912210716p:plain
リサイズ・移動後の最終的なパーティション構成。

  • 拡大前後のパーティション構成の詳細

 パーティションの拡大前後におけるgdiskのパーティション情報は、以下の通りです。

(拡大する前)
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048           63487   30.0 MiB    0700  Microsoft basic data
   2           63488          124927   30.0 MiB    0700  Microsoft basic data
   3          124928          186367   30.0 MiB    8300  Linux filesystem

(拡大した後)
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048           81919   39.0 MiB    0700  Microsoft basic data
   2           81920          163839   40.0 MiB    0700  Microsoft basic data
   3          163840          243711   39.0 MiB    8300  Linux filesystem
  • ディスク内容の確認

 ディスクの内容を確認します。今回は、それぞれのパーティションに入れた10個のファイル(乱数を書き込んだ1MiBのファイル)について、cmpやdiffコマンドを用いてバイナリーレベルで一致を確認しました。

6. ディスクを縮小する場合

 サイズが小さなディスクに移し替える場合です。

 オリジナルディスクの使用量は、ターゲットディスクよりも小さくなければならないことは言うまでもありませんが、ディスクパーティションも小さくしておく必要があります。

 このディスクパーティションの縮小には、初めにオリジナルディスクで行う場合と作業ディスクで行う場合とがあります。

 前者であれば、作業ディスクは不要です。

6.1 オリジナルディスクのパーティションを縮小する場合

 作業の開始前に、必ずオリジナルディスクのバックアップを取っておきます。

① オリジナルディスクのパーティションの縮小・移動

 ディスク拡大の場合と同様に、gpartedを用いてパーティションを縮小します。

 ここでは、それぞれのパーティションを20MiBに縮小し計80MiBにしようと思いましたが、gpartedはfat16の縮小には対応してなかったので*3、ntfs、ext4のパーティションのみを20MBに縮小します。なお、fat32であれば、縮小にも対応しています。

$ gparted /dev/sdS
f:id:toranosuke_blog:20180913125553p:plain
縮小後のオリジナルディスクのパーティション。

注意事項:  第2GPTテーブルをディスク末尾に配置しますので、ターゲットディスクの末尾に少なくとも1MiBの未割り当ての領域を確保しておく必要があります。

Windowsの場合
 Windowsの機能を用いて、パーティションのリサイズができます。

● 「アプリ」→「Windows管理ツール」→「コンピュータの管理」→「記憶域」→「ディスクの管理」

 具体的な操作方法は、例えば、次の記事に解説があります。

[FAQ番号:027519]Cドライブのサイズを変更(拡張/縮小)する方法(Windows 7 / Windows 8 / Windows 8.1)|FAQ Search|エプソンダイレクト

 実際には、「ディスクの管理」では、空きがあるにも関わらず、縮小できない場合があるようです*4

② ターゲットディスクへのコピー

 オリジナルディスクをターゲットディスクへコピーします。

$ dd if=/dev/sdS of=/dev/sdT
dd: writing to '/dev/sdT': No space left on device
163841+0 records in
163840+0 records out
83886080 bytes (84 MB, 80 MiB) copied, 3.93326 s, 21.3 MB/s

 ターゲットディスクよりも大きい部分のデータはコピーされません。ターゲットディスクが小さくて入りきらかなかったので、"No space left on device" のメッセージとともに、ddは終了します。

③ ターゲットディスクのGPTテーブルの修正

 ミラーリングの場合と同様に、gdiskで第2GPTテーブルをディスク末尾に配置します。

$ gdisk /dev/sdT
Command (? for help): x              (Expertメニューに入る)
Expert command (? for help):  e      (第2GPTテーブルをディスク末尾に移動)
Expert command (? for help):  w      (変更したテーブルをディスクに書き込む)
$ partprobe /dev/sdT

 これは、前述したgdiskによるGPTテーブルの末尾への移動の場合と同じコマンドです。しかし、今回は、第2GPTテーブルが存在しないため、ディスク末尾に新たに第2GPTテーブルが作成されます。

 また、gdiskコマンド実行中に、ディスクサイズが小さい、第2GPTテーブルが破壊されている等の警告メッセージが現れます。

f:id:toranosuke_blog:20180913140904p:plain
クローンでの最終的なパーティション構成。この例では、80MiBのディスクへ縮小。

④ ディスク内容の確認

 mountを行い、オリジナルディスクの内容とターゲットディスクの内容が一致するか、例えば、cmpコマンドやdiffコマンドを用いて確認します。

6.2 作業ディスクでパーティションを縮小する場合

 オリジナルディスクを作業ディスクにコピー後、作業ディスクのパーティションを縮小、ターゲットディスクに書き込みます。

① 作業ディスクへのコピー

 オリジナルディスクを作業ディスクにコピーします。

$ dd if=/dev/sdS of=/dev/sdW

② 作業ディスクのGPTテーブルの修正

 gdiskを用いて作業ディスクの第2GPTテーブルをディスク末尾に配置します。

$ gdisk /dev/sdW
Command (? for help): x              (Expertメニューに入る)
Expert command (? for help):  e      (第2GPTテーブルをディスク末尾に移動)
Expert command (? for help):  w      (変更したテーブルをディスクに書き込む)
$ partprobe /dev/sdW

③ 作業ディスクのパーティションの縮小・移動

 gpartedを用いて前述と同様に作業ディスクのパーティションを縮小・移動します。

 ターゲットディスクの末尾に少なくとも1MiBの未割り当ての領域できるように作業ディスクの末尾の未割り当て領域を調整します。

gparted /dev/sdW

④ 作業ディスクの内容をターゲットディスクにコピー

ddによって作業ディスクの内容をターゲットディスクにコピーします。

$ dd if=/dev/sdW of=/dev/sdT

 作業ディスクよりもターゲットディスクの容量が小さいので、警告メッセージがでますが、意図通りの動作です。

⑤ ターゲットディスクのGPTテーブルの修正

 gdiskでコマンドeを実行すると、ディスク末尾に第2GPTテーブルが新規に作成されます。

$ gdisk /dev/sdT
Command (? for help): x              (Expertメニューに入る)
Expert command (? for help):  e      (第2GPTテーブルをディスク末尾に移動)
Expert command (? for help):  w      (変更したテーブルをディスクに書き込む)
$ partprobe /dev/sdT

⑥ ディスク内容の確認

 mountを行い、オリジナルディスクの内容とターゲットディスクの内容が一致するか、例えば、cmpコマンドやdiffコマンドを用いて確認します。

7. まとめ

 今回は、Linuxにおけるクローンディスクの作り方について、リサイズする場合も含めて説明しました。今回説明した方法で、データが一致することは確認できました。

 しかし、この方法では、次のような点が原因となって不具合が発生するかもしれません。

  • 直接セクター配置によってアクセスするプログラムがある場合。
     特にブートに関連するプログラムには、このようなプログラムがあります。このため、システムディスクの起動関連のパーティションについては、リサイズ・移動をしない方が安心です。
  • ディスクのハードディスク情報を用いて動作している場合
     システムは、ハードディスクのシリンダー数やヘッド数、SSDなどの情報を用いています。これらの情報が誤っていれば、システムの動作に影響を与えるかもしれません。
  • ハードディスクにセキュリティ対策が施されている場合
     ハードウェアレベルでディスクに暗号化がかかっているのであれば、ディスクの中身を知ることはできませんので、パーティション変更等はできません。ミラーリングなどもできないようにセキュリティ保護される場合もあると思います。また、クローンディスクができたとしても、セキュアブートの設定を変更する必要があるかもしれません。

 システムディスクの場合、様々なトラブルに見舞われる可能性がありますので、オリジナルのディスクはそのままの状態で一切変更を加えずに、丸ごとコピーしたディスクをオリジナルディスクと見立てて、作業するとよいと思います。

(2018/9/13)

関連記事

*1:完全シャットダウンは、Shiftキー+「シャットダウン」をクリック、あるいは、"shutdown /s /t 0"

*2:gdiskでアライメント単位を変更することができるようなので、それを変更すれば、gpartedのアライメントの単位も変わるのかもしれません(未確認)。

*3:gpartedがサポートしているファイルシステムは、「表示」→「サポートするファイルシステム(F)」から確認できます。これによれば、fat16の縮小にも対応しているはずですが、一定以上のファイルサイズであることなどの条件があるのかもしれません。

*4:https://www.disk-partition.com/jp/articles/unable-to-shrink-c-drive.html