ディレクトリの内容のMD5の合計を合計として取得するには?

ディレクトリの内容のMD5の合計を合計として取得するには?

md5sumプログラムはディレクトリのチェックサムを提供しません。サブディレクトリのファイルを含むディレクトリの内容全体の単一のMD5チェックサムを取得したいと思います。つまり、すべてのファイルで構成される結合チェックサムです。これを行う方法はありますか?

答え1

正しいアプローチは、質問する正確な理由によって異なります。

オプション1:データのみ比較

ツリーファイルの内容のハッシュのみが必要な場合は、次のことができます。

$ find -s somedir -type f -exec md5sum {} \; | md5sum

まず、すべてのファイルコンテンツを予測可能な順序で個別に集計してから、ファイル名のリストとMD5ハッシュを渡してハッシュし、ツリーの変更時にファイルコンテンツが変更されたときにのみ変更される値を提供します。

残念ながら、find -sBSD find(1)はmacOS、FreeBSD、NetBSD、およびOpenBSDでのみ機能します。 GNUまたはSUS find(1)システムで同様のものを得るには、もっと醜いことが必要です。

$ find somedir -type f -exec md5sum {} \; | sort -k 2 | md5sum

このビットはfind -sMD5ハッシュをスキップするように指示するため、計算ではフィールド2のファイル名だけが行末までソートされます。sort-k 2sort

このコマンドバージョンの1つの弱点は、改行文字を含むファイル名があると複数行の呼び出しのように見えるため、簡単に混乱する可能性があることですsort。このfind -sバリエーションは、ツリーの巡回とソートが同じプログラムで発生するため、この問題はありませんfind

どちらの場合も、誤った肯定を避けるために順序が必要です。ほとんどの一般的なUnix / Linuxファイルシステムは、ディレクトリリストを安定した予測可能な順序で維持しません。lsなどを使用して認識できない場合があります。ディレクトリの内容を自動的にソートします。何らかの方法で出力の順序を指定しない呼び出しは、出力の行の順序がデフォルトのファイルシステムが返す順序findと一致します。これにより、ファイルの順序が次のように指定された場合にコマンドが提供されます。入力が変更されます。データが同じでもハッシュ値が変更されました。

-k 2GNUsortコマンドで上記のビットが必要かどうかを尋ねることができます。ファイル名は、内容が変更されない限りファイルデータのハッシュとして適切に表示されるため、このオプションを削除すると偽の肯定が発生しないため、GNUとBSDで同じコマンドを使用できますsort。ただし、ハッシュ競合がある場合、ファイル名の正確な順序がこの操作を実行しない場合、提供された部分順序と一致しない可能性が低くなります(MD5は1:2 128-k 2)。ただし、このような小さな矛盾の可能性がアプリケーションにとって重要な場合は、全体的なアプローチが不可能になる可能性があることに注意してください。

md5sumコマンドをmd5別のハッシュ関数に変更する必要があるかもしれません。別のハッシュ関数を選択してシステムにコマンドの2番目の形式が必要な場合は、sortそれに応じてコマンドを調整する必要があります。別のトラップは、一部のデータ要約プログラムがファイル名をまったく作成しないことです。典型的な例は古いUnixsumプログラムです。

この方法はmd5sumN + 1呼び出しを必要とするので、やや非効率的です。ここで、Nはツリー内のファイル数ですが、これはファイルとディレクトリのメタデータハッシュを防ぐために必要なコストです。

オプション2:データ比較そしてメタデータ

これを検出できる必要がある場合何もないファイルの内容だけでなく、ツリーの内容も変更されました。tarディレクトリの内容を圧縮して、次のアドレスに送信するように依頼してくださいmd5sum

$ tar -cf - somedir | md5sum

ファイル権限、所有権なども表示できるため、tarファイル内容の変更だけでなく、これらの変更も検出します。

この方法は、ツリーを一度だけナビゲートし、ハッシュプログラムを一度だけ実行するので、はるかに高速です。

find上記のベース方法と同様に、tarファイル名はデフォルトのファイルシステムから返された順序で処理されます。おそらく、アプリケーションにはこれが起こらないと確信できる内容があります。この場合、少なくとも3つの異なる使用パターンを考えることができます。 (指定されていない動作領域に入っているため、リストしません。ここにあるすべてのファイルシステムは、オペレーティングシステムのバージョンによって異なる場合があります。)

誤検出が発生した場合は、次のfind | cpioオプションをお勧めします。ザイルズの答え

答え2

チェックサムは、ファイルの決定的で明確な文字列表現でなければなりません。重要なのは、同じファイルを同じ場所に置くと同じ結果が得られることを意味します。明示的とは、表現が異なる2つの異なるファイルセットを意味します。

データとメタデータ

これらのファイルを含むアーカイブを作成するのは良いスタートです。これは明らかなマークです(明らかにアーカイブを抽出してファイルを回復できるからです)。日付や所有権などのファイルメタデータを含めることができます。しかし、これはまだ完全に正しくありません。アーカイブの表現は、ファイルが保存されている順序と圧縮(該当する場合)によって異なりますので、アーカイブはあいまいです。

解決策は、アーカイブする前にファイル名をソートすることです。ファイル名に改行文字が含まれていない場合は、リストを実行してfind | sortその順序でアーカイブに追加できます。アーカイバにディレクトリに再帰しないように指示することに注意してください。以下は、POSIX pax、GNU tar、およびcpioの例です。

find | LC_ALL=C sort | pax -w -d | md5sum
find | LC_ALL=C sort | tar -cf - -T - --no-recursion | md5sum
find | LC_ALL=C sort | cpio -o | md5sum

名前と内容だけ、ローテク方式で

メタデータではなくファイルデータのみを考慮したい場合は、ファイルの内容のみを含むアーカイブを作成できますが、これを達成するための標準ツールはありません。ファイルの内容を含める代わりに、ファイルのハッシュを含めることができます。ファイル名に改行文字が含まれておらず、通常のファイルとディレクトリのみがある場合(シンボリックリンクや特殊ファイルはありません)、これは非常に簡単ですが、いくつかの点に注意する必要があります。

{ export LC_ALL=C;
  find -type f -exec wc -c {} \; | sort; echo;
  find -type f -exec md5sum {} + | sort; echo;
  find . -type d | sort; find . -type d | sort | md5sum;
} | md5sum

チェックサムリストに加えて、ディレクトリリストも含まれます。それ以外の場合、空のディレクトリは表示されません。ファイルのリストが並べ替えられています(特定の再現可能なロケールで - これを思い出してくれたPeter.Oに感謝します)。echoこれらの2つの部分を分離します(この部分がない場合は、出力md5sumが通常のファイルにも渡されるように見える名前で空のディレクトリを作成できます)。また、避けるべきファイルサイズもリストされています。長さ拡張攻撃

ところで、MD5は廃止されました。可能であれば、SHA-2を使用するか、少なくともSHA-1を使用することを検討してください。

名前とデータ、名前に改行をサポート

以下は、GNUツールを使用してファイル名をヌルバイトで区切る上記のコードのバリエーションです。これにより、ファイル名に改行文字を含めることができます。 GNUダイジェストユーティリティは、あいまいな改行が発生しないように出力から特殊文字を引用します。

{ export LC_ALL=C;
  du -0ab | sort -z; # file lengths, including directories (with length 0)
  echo | tr '\n' '\000'; # separator
  find -type f -exec sha256sum {} + | sort -z; # file hashes
  echo | tr '\n' '\000'; # separator
  echo "End of hashed data."; # End of input marker
} | sha256sum

より強力なアプローチ

これは、ファイル階層を記述するハッシュを構築するための最小限のテスト済みPythonスクリプトです。ディレクトリとファイルの内容を説明し、シンボリックリンクやその他のファイルを無視してファイルを読み取れない場合は、致命的なエラーを返します。

#! /usr/bin/env python
import hashlib, hmac, os, stat, sys
## Return the hash of the contents of the specified file, as a hex string
def file_hash(name):
    f = open(name)
    h = hashlib.sha256()
    while True:
        buf = f.read(16384)
        if len(buf) == 0: break
        h.update(buf)
    f.close()
    return h.hexdigest()
## Traverse the specified path and update the hash with a description of its
## name and contents
def traverse(h, path):
    rs = os.lstat(path)
    quoted_name = repr(path)
    if stat.S_ISDIR(rs.st_mode):
        h.update('dir ' + quoted_name + '\n')
        for entry in sorted(os.listdir(path)):
            traverse(h, os.path.join(path, entry))
    elif stat.S_ISREG(rs.st_mode):
        h.update('reg ' + quoted_name + ' ')
        h.update(str(rs.st_size) + ' ')
        h.update(file_hash(path) + '\n')
    else: pass # silently symlinks and other special files
h = hashlib.sha256()
for root in sys.argv[1:]: traverse(h, root)
h.update('end\n')
print h.hexdigest()

答え3

2つのディレクトリ間の違いを見つけることが目標である場合は、diffの使用を検討してください。

この試み:

diff -qr dir1 dir2

答え4

使用checksumdir:

$ pip install checksumdir
$ checksumdir -a md5 assets/js
981ac0bc890de594a9f2f40e00f13872
$ checksumdir -a sha1 assets/js
88cd20f115e31a1e1ae381f7291d0c8cd3b92fad

急いでそして簡単に他のbashソリューションよりも。

関連情報