dd

提供: ArchWiki
2023年5月19日 (金) 18:18時点におけるKgx (トーク | 投稿記録)による版 (→‎ディスク関連や他の使用場面: 翻訳)
ナビゲーションに移動 検索に移動

関連記事

dd はファイルの変換とコピーを主な目的とする コアユーティリティ です。

cp と同様にデフォルトでは dd はファイルのビットごとのコピーを作成しますが、低レベルの I/O フロー制御機能を備えています。

詳細は、dd(1) またはフルドキュメントを参照してください。

ヒント: デフォルトでは、dd はタスクが完了するまで何も出力しません。操作の進行状況を監視するには、コマンドに status=progress オプションを追加します。
警告: この種のコマンドはどれも、データを不可逆的に破壊する可能性があるため、dd の使用には細心の注意を払う必要があります。

インストール

dd は GNU coreutils の一部です。このパッケージ内の他のユーティリティについては、Core utilities を参照してください。

ディスクの複製と復元

dd コマンドはシンプルでありながら多機能で強力なツールです。ファイルシステムの種類や OS に関係なく、コピー元からコピー先へブロック単位でコピーすることができます。ライブ CD のようなライブ環境から dd を使用するのが便利です。

警告: このタイプのコマンドと同様に使用時には十分な注意が必要です。さもないと、データが破壊される可能性があります。入力ファイル (if=) と出力ファイル (of=) の順番を覚えておいて、逆にしないでください。出力先のドライブやパーティション (of=) のサイズが、入力元 (if=) と同じかそれ以上である事を常に確認してください。

パーティションの複製

物理ディスク /dev/sda のパーティション 1 から、物理ディスク /dev/sdb のパーティション 1 へ:

# dd if=/dev/sda1 of=/dev/sdb1 bs=64K conv=noerror,sync status=progress
ノート: 出力パーティション of= (この例では sdb1) が存在しない場合、dd はこの名前のファイルを作成し、ルートファイルシステムをいっぱいにしてしまうので注意が必要です。

ハードディスク全体の複製

物理ディスク /dev/sda から物理ディスク /dev/sdb へ:

# dd if=/dev/sda of=/dev/sdb bs=64K conv=noerror,sync status=progress

このコマンドは、パーティションテーブル、ブートローダー、すべてのパーティション、UUID、データを含むドライブ全体を複製します。

  • bs= はブロックサイズを設定します。デフォルトは512バイトで、これは1980年代前半以降のハードドライブの「古典」的なブロックサイズですが、最も便利なものではありません。64KBや128KBなど、より大きな値を使用してください。また、「ブロックサイズ」だけでなく、読み取りエラーの伝搬にも影響を与えるため、下記の警告をお読みください。詳細は、[1][2] を参照して、最適な bs 値を見つけてください。
  • noerror はすべての読み取りエラーを無視して操作を続けるように dd に指示します。dd のデフォルトの動作は、いかなるエラーでも停止します。
  • sync は読み込みエラーがあった場合、入力ブロックをゼロで埋め、データのオフセットは同期されたままになります (読み込みエラーが疑われる場合、sync を用いた際の読み込みエラーの挙動に関する下記の詳細な説明を見てください)。
  • status=progress は、操作がいつ完了するかを推測するために使用できる転送統計を表示します。
ノート: 指定するブロックサイズは、読み取りエラーの処理方法に影響します。以下をお読みください。データの回復には、ddrescue を使用してください。

dd ユーティリティには、技術的には「入力ブロックサイズ」(IBS)と「出力ブロックサイズ」(OBS)があります。bs を設定すると、実質的に IBS と OBS の両方を設定することになります。通常、ブロックサイズが例えば 1MiB の場合、dd は 1024×1024 バイトを読み込み、同じバイト数を書き込みます。しかし、読み取りエラーが発生すると、事態はおかしくなります。多くの人は、noerror,sync オプションを使うと、dd が「読み込みエラーをゼロで埋める」と思っているようですが、そうではありません。ドキュメントによると、dd は読み込み完了後に OBS から IBS のサイズを埋める、つまりブロックの最後にゼロを追加するのです。つまり、ディスクの場合、512 バイトの読み取りエラーが読み取りの最初に1回発生しただけで、事実上 1MB 全体がめちゃくちゃになってしまうのです: 12ERROR89 は 120000089 ではなく 128900000 となります。

ディスクにエラーがないことが確認できれば、ブロックサイズを大きくしてコピーを進めることができ、コピーの速度が数倍向上します。例えば、Celeron 2.7GHz のシステムで、bs を 512 から 64K に変更すると、コピー速度が 35MB/s から 120MB/s になります。ただし、コピー元のディスクで発生した読み取りエラーは、コピー先のディスクではブロックエラーとして発生することに注意してください。つまり、512バイトのリードエラーは、出力先の 64 KiB ブロック全体をめちゃくちゃにします。

ヒント: dd の進行状況を表示するためには、status=progress オプションを使用してください。詳細については、dd(1) を参照してください。
ノート:
  • ext2/3/4 ファイルシステムの UUID を一意な状態に戻すには、すべてのパーティションで、tune2fs /dev/sdXY -U random を使用してください。スワップパーティションの場合は、代わりに、mkswap -U random /dev/sdXY を使ってください。
  • GPT ディスクを複製する場合、sgdisk を使うことで、ディスクとパーティションの GUID をランダム化し、GUID の一意性を保つことができます。
  • dd によるパーティションテーブルの変更はカーネルには登録されません。再起動せずに変更を通知するには、partprobe (GNU Parted の一部)のようなユーティリティを使ってください。

パーティションテーブルのバックアップ

fdisk#パーティションテーブルのバックアップとリストア または gdisk#パーティションテーブルのバックアップとリストア を参照。

ディスクイメージの作成

ライブ環境のメディアから起動し、ソースのハードドライブのパーティションがマウントされていないことを確認してください。

次に、外部のハードドライブをマウントし、ドライブをバックアップしてください:

# dd if=/dev/sda conv=sync,noerror bs=64K | gzip -c  > /path/to/backup.img.gz

必要であれば (出力ファイルが FAT32 ファイルシステム上に保存される場合など)、ディスクイメージを複数に分けてください (split(1) も見てください):

# dd if=/dev/sda conv=sync,noerror bs=64K | gzip -c | split -a3 -b2G - /path/to/backup.img.gz

ローカルに十分なディスクスペースがない場合、イメージを ssh を通して送ることもできます:

# dd if=/dev/sda conv=sync,noerror bs=64K | gzip -c | ssh user@local dd of=backup.img.gz

最後に、イメージに保存されているパーティションテーブルを解釈するために必要な、ドライブのジオメトリ情報に関する追加情報を保存します。最も重要なのはシリンダーサイズです。

# fdisk -l /dev/sda > /path/to/list_fdisk.info
ノート: バックアップする HD のキャッシュの容量と同じブロックサイズ (bs=) を使うと良いかもしれません。例えば、bs=8192K は 8 MiB キャッシュでうまく行きます。この記事で説明されている 64 KiB は、デフォルトの bs=512 バイトよりも良いですが、より多くの bs= を使えばより速く実行できます。

{{Tip|gzip はデータ圧縮にシングルの CPU コアしか使用できません。そのせいで、データのスループットが最近のストレージの書き込みスピードよりかなり遅くなってしまいます。マルチコア圧縮でディスクイメージの作成をより速くするには、例えば pigz などのパッケージをインストールし、上記の gzip -c コマンドを pigz -c に置き換えてください。巨大なディスクでは時間を節約できるかもしれません。また、他の圧縮アルゴリズム (zstd など) を試しても良いかもしれません。

システムの復元

システムを復元するには:

# gunzip -c /path/to/backup.img.gz | dd of=/dev/sda

イメージが分割されている場合は、代わりに以下を使用してください:

# cat /path/to/backup.img.gz* | gunzip -c | dd of=/dev/sda

MBR のバックアップと復元

ディスクに変更を加える前に、ドライブのパーティションテーブルとパーティションスキームをバックアップしておくと良いでしょう。また、同じパーティションレイアウトを複数のドライブにコピーするためにバックアップを使うこともできます。

MBR はディスクの先頭 512 バイトに格納されています。MBR は4つの部分から成ります:

  1. 始めの 440 バイトにはブートストラップコード (ブートローダ) が含まれています。
  2. 次の 6 バイトにはディスクのシグネチャが含まれています。
  3. 次の 64 バイトにはパーティションテーブルが含まれています (各16バイトの4つのエントリ、各プライマリパーティションに1つのエントリ)。
  4. 最後の 2 バイトにはブートシグネチャが含まれています。

MBR を mbr_file.img として保存するには:

# dd if=/dev/sdX of=/path/to/mbr_file.img bs=512 count=1

完全な dd ディスクイメージから MBR を取り出すこともできます:

# dd if=/path/to/disk.img of=/path/to/mbr_file.img bs=512 count=1

バックアップから MBR を復元するには (注意。このコマンドは既存のパーティションテーブルを破壊し、ディスク上のすべてのデータにアクセスできなくなります):

# dd if=/path/to/mbr_file.img of=/dev/sdX bs=512 count=1
警告: 一致しないパーティションテーブルを使って MBR を復元すると、データを読み出せなくなり、ほぼ復元不可能になります。ブートローダを再インストールしたいだけならば、ブートローダは DOS 互換領域も実装しているので、そのブートローダの関連するページを見てください: GRUBSyslinux

ブートローダを復元したいだけで、プライマリパーティションテーブルのエントリに興味はないならば、単に MBR の先頭 440 バイトを復元すれば良いだけです:

# dd if=/path/to/mbr_file.img of=/dev/sdX bs=440 count=1

パーティションテーブルだけを復元するには、以下のコマンドを使う必要があります:

# dd if=/path/to/mbr_file.img of=/dev/sdX bs=1 skip=446 count=64

ブートローダーの削除

MBR ブートスラップコードを消去するには(別のオペレーティングシステムを完全に再インストールする必要がある場合に役立つ場合があります)、最初の440バイトのみをゼロにする必要があります:

# dd if=/dev/zero of=/dev/sdX bs=440 count=1

ディスク関連や他の使用場面

既にお気づきの方もいらっしゃるかもしれませんが、dd(1) コアユーティリティは他のユーティリティと比較して かなり異なる コマンドラインのシンタックスを持っています。さらに、他のコモディティユーティリティにはないユニークな機能 をサポートしていますが、特定のシナリオに適用すると、あまり理想的でないpotential error-prone デフォルト動作 (時には、できないこと) がいくつも存在します。そのため、ユーザーは dd コアユーティリティの代わりに、ある面ではより優れた代替品を使いたいと思うかもしれません。

そうは言っても、ddcore utility であり、Arch や他の多くのシステムにデフォルトでインストールされているため、代替ユーティリティやより専門化されたユーティリティよりも好ましいことに注意する必要があります。システムに新しいパッケージをインストールするのは不便です。

上記で説明した 2 つの側面をカバーするために、このセクションでは、他の汎用ユーティリティではほとんど見られない dd(1) コア ユーティリティの機能を、pacman/Rosetta の記事に似た形式で要約することに専念します。ただし、サンプルの量は、dd のこれらの機能を調べるのに十分な量 (ヒント:ie または To 節で示されています) にできる限り少なく、単純に削減されています。サブセクションの下のボックス)、実践または疑似コードのいずれかにおいてです.

ノート: この専用セクションを適切な長さに保つために、代替ユーティリティの比較には、公式リポジトリにあるパッケージのみが含まれています。この場合、言及されている他のユーティリティよりも明らかな利点があり、必要に応じて詳細が記載されています。
その他の代替ユーティリティについては、coreutils#dd の代替案 を参照して下さい。

バイナリファイルにブロック単位のパッチをインプレースに適用する

自動シェルスクリプトで機能が限定されたバイナリファイルパッチャーとして dd を使用することは珍しいことではありません。これは、書き込み前に指定されたオフセットによって出力ファイルを seek することができ、ブロックすることができるためです。 conv=notrunc オプションを追加して、出力ファイルにブロック単位 (または bs=1 の場合はバイト y バイト) でインプレースパッチを適用します。

たとえば、cpio(5) § Portable ASCII Format アーカイブ内の最初のメンバーのタイムスタンプ部分を変更するには、ファイルの 49 番目のバイトから始まります (または、次の場合は 0x30 のオフセットで始まります)。16 進表記を好む場合):

$ touch a-randomly-chosen-file
$ bsdtar -cf example-modify-ts.cpio --format odc -- a-randomly-chosen-file
$ printf '%011o' "$(date -d "2019-12-21 00:00:00" +%s)" | dd conv=notrunc of=example-modify-ts.cpio seek=48 oflag=seek_bytes
ノート: 現在文書化されていない seek_bytes 出力フラグ は、出力への write(2) を開始する前にブロックではなくバイトのオフセットで出力をシークするために上記に追加されています。
ヒント: コマンドライン入力の 16 進表記からバイト ストリームを出力するには、basenc(1) § base16 および/または printf(1) を使用します。
ヒント: この機能では、dd の代わりに、シェルが開いたファイルディスクリプタに対して lseek(2) を呼び出すことをサポートするシェルを使用することを検討することができます。:
  • dd の入力ファイルは splice(2) システムコールを利用するプログラムと接続されたパイプであり、ユーザはより良いパフォーマンスのために dd の不必要なユーザ空間 I/O を避けたいと考えています。
  • または、シェルスクリプトのループ内で頻繁に fork(2) が発生するのを避け、パフォーマンスペナルティを下げたい。
その場合、まずそのシェルにファイルディスクリプタを開かせ、そのファイルディスクリプタに対してシーク操作を行い、splice(2) システムコールを使う対応するユーティリティの出力端としてこのファイルディスクリプタを割り当てる必要があります(あるいはzshmodules(1) § sysseek のようにフォークしないシェル内蔵コマンドもあります):
$ zsh
$ local +xr openToWriteFD
$ zmodload zsh/system
$ sysopen -wo cloexec -u openToWriteFD example-modify-ts.cpio
$ sysseek -u $openToWriteFD 48
$ printf '%011o' "$(date -d "2019-12-21 00:00:00" +%s)" >&${openToWriteFD}
警告: オフセットを使用して書き込む必要があるプログラムが実際に splice(2) を使用していることを確認できない場合は、このアプローチの使用を避けてください (これは、プログラムが出力に対していかなる種類のシークや切り捨ても実行しないことを意味します)。一部のプログラムは、この動作がコマンド ラインフラグで指定されていない場合でも、入力/出力ファイル記述子を自動的にシーク/切り捨てることがあります。これにより、シェルの lseek(3) 呼び出しが無効になったり、開いているファイル記述子が予期せず切り詰められたりすることがあります。

VFAT ファイルシステムイメージのボリュームラベルを表示する

ヒント: この特定のシナリオでは、より現実的な選択肢は file です。

ファイルシステム VFAT のボリュームラベル イメージファイルを読み取るには、ASCII スペースが埋め込まれた全長 11 バイトで、オフセットは 0x047 です。:

$ truncate -s 33M empty-hole.img
$ fatlabel empty-hole.img LabelMe
$ dd iflag=skip_bytes,count_bytes count=11 skip=$((0x047)) if=empty-hole.img | sed -e 's% *$%%'
ノート: 両方の入力フラグは現在文書化されていません:
  • 以前の skip_bytes は、入力ファイルから read(2) を開始する前に、ブロック数ではなくバイト数でオフセットしてシーク (シークできない入力の場合は skip) するように dd に指示しました。
  • count_bytes では、ブロック数ではなく、入力ファイルからコピーするブロックの総量を バイト で指定することができます。このオプションを指定しても部分的な read(2) の対象となる可能性があるので混乱するかもしれませんが、この挙動をより理解するために input block count の小数値と考えるようにします。
ヒント: 指定された長さ内で入力 (オフセット付き) から出力にデータを転送するには、シェル スクリプトでは、代わりに範囲表記を使用する代替手段として curl(1) § r, を考慮することもできます。
ノート: curl は、入力ファイルがデバイス/パイプである場合のシーク/スキップをサポートしません。別の代替手段 socat(1) は、入力ファイルに対するこれらの操作をサポートします (ブロックデバイスを含む、パイプを除く) 文字デバイス) ですが、curl ほどコモディティ化されていません。:
$ socat -u -,seek=$((0x047)),readbytes=11 - < empty-hole.img | sed -e 's% *$%%'

パイプで繋がれたコマンド間で sponge する

ヒント: タイトルですでに述べたように、この特定のシナリオでの実際的な選択肢は ${TMPDIR:-/tmp} への書き込みによるアトミック書き込みをサポートする sponge(1) です

次の例では、出力側のブロックが予想より長くなった場合に、入力側で不必要に長く続く TCP 接続を避けるために、2 つのコマンドの間に dd を挿入し、出力ブロックサイズは入力よりも確実に大きく、それでも使用可能なメモリよりはかなり小さいものにすることができます。

$ curl -qgsfL http://example.org/mirrors/ftp.archlinux.org/mirrored.md5deep | dd ibs=128k obs=200M | poor-mirroring-script-that-perform-mirroring-on-input-paths-line-by-line-wo-buffer-entire-list-first
警告: dd はコピー操作全体を開始する前に出力ファイルを切り詰めるため、これを sponge(1) の一般的な代替手段として考慮しないでください。

サイズの制限付きでデータを転送する

データストリーミングのシェルスクリプトで dd を使用すると、パイプコマンドで消費されるデータの合計長を制限することができるのは一般的です。例えば、ustar ヘッダブロック (tar(5) § POSIX ustar Archives) をシェルスクリプトの機能を使ってストリーミング方式で検査する場合です:

ノート: count オプションの引数にある B サフィックスは GNU coreutils v9.1 の 新しく導入された 機能で、 dd#VFAT ファイルシステムイメージのボリュームラベルを表示する と同じ効果を持っている。 この機能は、count=256kのように、dd がバイトではなく262144個の入力ブロックをコピーすることを示すオプションと混同される可能性があります。
hexdump-field() {
  set -o pipefail
  printf '%s[%d]:\n' $1 $2
  dd count=${2}B status=none | hexdump -e $2'/1 "%3.2x"' -e '" | " '$2'/1 "%_p" "\n"'
}

inspect-tar-header-block() {
  local -a hdrstack=(
    name 100
    mode 8
    uid 8
    gid 8
    size 12
    mtime 12
    checksum 8
    typeflag 1
    linkname 100
    magic 6
    version 2
    uname 32
    gname 32
    devmajor 8
    devminor 8
    prefix 155
    pad 12
  )
  set - ${hdrstack[@]}
  while test $# -gt 0; do
    hexdump-field $1 $2 || return
    shift 2
  done
}
$ bsdtar -cf - /dev/tty /dev/null 2>&- | dd count=1 skip=1 status=none | inspect-tar-header-block
ヒント: 指定された長さ内で入力から出力にデータをストリーミングするには という代替案は、splice(2) システムコールをサポートする pv(1) § S, です。
ノート: もう 1 つの代替候補は head(1) § c ですが、GNU coreutils 以外の実装 と glibc は消費する可能性があります要求されたデータよりも多くのデータ] が発生し、ストリーミングシェルスクリプトでデータの不整合の問題が発生します。
ヒント: 上記の機能グリッドに加えて、入力ファイルがストリーミング前に特定のオフセットによって lseek(2) され、dd の出力端がプログラムに接続されたパイプである場合は、splice(2) の場合、代わりに次の使用を検討できます。 次の bash の例のように pv(1) § S, (前述) を実行します (ファイル記述子が ls -l /proc によってシェルに割り当てられていないと仮定します) 最初は bash の /self/fd
$ bash
$ exec 9<dummy-but-rather-large.img
$ xxd -g 0 -l 0 -s $((0x47ffff)) <&9
$ pv -qSs 104857601200 <&9 | program-that-process-load-of-data-but-does-not-limit-read-length-as-desired-nor-support-offset-read
$ exec 9<&-
ノート: POSIX および一部の非 GNU 実装とは互換性がありませんが、上記の例の xxd(1) § s の使用を、count=0 と組み合わせた dd に置き換えることは可能です。および skip オプション coreutils テストスイートの例

ブータブルなディスクイメージをブロックデバイスに書き込み、任意で進捗情報を表示する

その場合に最も適合性が低い可能性がある dd を含むコモディティ ユーティリティの例については、USB インストールメディア#基本的なコマンドラインユーティリティを使う を参照してください。

ヒント: ファイルの内容をブロックデバイスに書き込む (進行状況インジケーター付き) という代替案として、dd_rescue(1) § W が推奨されます。デバイス上の古いバージョンのイメージを新しいバージョンで上書きする場合、不要な書き込みを回避できます。

トラブルシューティング

部分読み取り: コピーされたデータは要求されたデータよりも小さい

[3] のように、現時点で完全な入力ブロックが利用できない場合、dd で作成されたファイルは要求されたサイズよりも小さくなる可能性があります。per ドキュメント:

さらに、データ変換する conv 被演算子 (つまり、この Wiki 記事のようなオプション) が指定されていない場合、入力は読み取られるとすぐに出力にコピーされます。ブロックサイズより小さいです。

Linuxでは、read(2) システムコールは pipe(7) から読むとき、または /dev/urandom/dev/random などのデバイスファイルを読むときに早く戻ることがあります (つまり、部分読み) (例えば、基礎となるカーネルデバイスドライバーのハードコーディングされた制限 またはエントロピーが不十分です) ここで n は出力にコピーする入力ブロックの最大数 (潜在的な部分) を制限します。

可能性はありますが、保証はできません。dd がそのような種類の問題について警告する可能性があります。

dd: warning: partial read (X bytes); suggest iflag=fullblock

解決策は、警告にあるように、入力ファイルオプションに加えて iflag=fullblock オプションを dd コマンドに追加することです。たとえば、合計長が 40 MB のランダム データで満たされた新しいファイルを作成するには、次のようにします。

$ dd if=/dev/urandom of=new-file-filled-by-urandom.bin bs=40M count=1 iflag=fullblock
ノート: パイプや特殊なデバイスのファイルから読み込む際に、count=n オプションを指定して、ファイルの一部を固定長でコピーすることが推奨されます、また、デバイスの一部ファイル完全消去 する場合は、iflag=fullblock オプションを追加してdd コマンドで行うことを常に強くお勧めします。

パイプから読み込む場合、iflag=fullblock代替案 として、bslinux/limits.h で定義されている PIPE_BUF 定数に制限して pipe(7) の I/O を原子化します。例えば、全長5メガバイトのランダムな英数字で埋め尽くされたテキストファイルを用意する場合、以下のようになります:

$ LC_ALL=C tr -dc '[:alnum:]' </dev/urandom | dd of=passtext-5m.txt bs=4k count=1280

出力ファイルはパイプではないため、(入力) パイプと (出力) ディスク上のファイル。たとえば、出力ファイルのより効率的なブロック サイズを設定するには、次のようにします。

$ LC_ALL=C tr -dc '[:alnum:]' </dev/urandom | dd of=passtext-5m.txt ibs=4k obs=64k count=1280
ヒント: 状況によっては、出力ブロックサイズを入力ブロックサイズと同じ値に保つだけで、PIPE_BUF 定数で定義された値がすでに最適である場合があります。

総転送バイト数の読み出しが間違っている

以下の概念実証のように、出力への書き込み時にエラーが発生した場合 (例えば、SIGPIPE、欠陥のある媒体、または誤ってターゲットネットワークブロックデバイスを切断したことによる 部分書き込み)、読み出された総転送バイト数は実際よりも大きくなる可能性があります:第2の dd は明らかに512200バイト以上を読みませんが、第1の dd インスタンスはまだ不正確なバイト数512400バイトを報告します:

$ yes 'x' | dd bs=4096 count=512400B | dd ibs=1 count=512200 status=none >/dev/null
125+1 records in
125+1 records out
512400 bytes (512 kB, 500 KiB) copied, 10.7137 s, 47.8 kB/s

上記の PoC のように中断された転送を再開する場合、"+" 記号の前の数字で示されるように、既にコピーされた全出力ブロックの数の読み出しのみに依存することをお勧めします。

ノート: iflag=fullblock オプションを追加しても、部分I/Oブロック数が1より大きい場合は、部分 I/O が2回以上発生したことになります。 この場合、確実に転送を再開するために、次のことをお勧めします:
  • 潜在的な欠陥媒体の部分読み出しに柔軟に対処するために、代わりに ddrescue を使用して転送を再実行する。
  • ネットワーク接続が悪い状態で nbd に書き込む場合、dd_rescue(1) を使って直接 I/O で転送を再実行する。
  • 欠陥のある媒体への書き込みを避けることができます。