디렉토리 내용의 MD5 합계를 하나의 합계로 얻으려면 어떻게 해야 합니까?

디렉토리 내용의 MD5 합계를 하나의 합계로 얻으려면 어떻게 해야 합니까?

md5sum 프로그램은 디렉토리에 대한 체크섬을 제공하지 않습니다. 하위 디렉터리의 파일을 포함하여 디렉터리의 전체 내용에 대한 단일 MD5 체크섬을 얻고 싶습니다. 즉, 모든 파일로 구성된 하나의 결합된 체크섬입니다. 이를 수행할 수 있는 방법이 있습니까?

답변1

올바른 방법은 질문하는 이유에 따라 다릅니다.

옵션 1: 데이터만 비교

트리 파일 내용의 해시만 필요한 경우 다음과 같이 하면 됩니다.

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

먼저 모든 파일 내용을 예측 가능한 순서로 개별적으로 요약한 다음 해당 파일 이름 목록과 MD5 해시를 자체적으로 해시하여 전달하여 트리에 있는 파일 중 하나의 내용이 변경될 때만 변경되는 단일 값을 제공합니다.

안타깝게도 find -smacOS, FreeBSD, NetBSD 및 OpenBSD에서 사용되는 BSD find(1)에서만 작동합니다. GNU 또는 SUS find(1)를 사용하여 시스템에서 비슷한 것을 얻으려면 좀 더 추악한 것이 필요합니다.

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

find -s에 호출을 추가하여 BSD의 동작을 모방했습니다 sort. 비트 는 MD5 해시를 건너뛰도록 지시하므로 의 계산에 -k 2따라 필드 2에 있는 파일 이름만 줄 끝까지 정렬합니다 .sort

이 버전의 명령에는 약점이 있습니다. 즉, 파일 이름에 개행 문자가 있으면 혼동되기 쉽습니다. 왜냐하면 호출 시 여러 줄처럼 보이기 때문입니다 sort. 변형 find -s에는 트리 순회 및 정렬이 동일한 프로그램 내에서 발생하기 때문에 이러한 문제가 없습니다 find.

두 경우 모두 잘못된 긍정을 방지하려면 정렬이 필요합니다. 가장 일반적인 Unix/Linux 파일 시스템은 디렉터리 목록을 안정적이고 예측 가능한 순서로 유지하지 않습니다. ls디렉토리 내용을 자동으로 정렬하는 등의 기능을 사용하면 이 사실을 깨닫지 못할 수도 있습니다 . 어떤 방식으로든 출력을 정렬하지 않고 호출하면 find출력의 줄 순서가 기본 파일 시스템이 반환하는 순서와 일치하게 됩니다. 이로 인해 입력으로 제공된 파일의 순서가 변경되면 이 명령이 변경된 해시 값을 제공하게 됩니다. 데이터가 동일하더라도.

-k 2위의 GNU sort명령에 있는 비트가 필요한지 물어볼 수도 있습니다 . 파일 데이터의 해시가 내용이 변경되지 않는 한 파일 이름에 대한 적절한 프록시라는 점을 감안할 때 이 옵션을 삭제하면 거짓 긍정이 발생하지 않으므로 GNU 및 BSD에서 동일한 명령을 사용할 수 있습니다 sort. 그러나 파일 이름의 정확한 순서가 해시 충돌이 있는 경우 없이 수행할 수 있는 부분 순서와 일치하지 않을 가능성이 적습니다(MD5의 경우 1:2 128-k 2 ). 그러나 불일치 가능성이 애플리케이션에 중요하다면 이 모든 접근 방식은 아마도 불가능할 것입니다.

명령을 다른 해시 함수 md5sum로 변경해야 할 수도 있습니다 . md5다른 해시 함수를 선택하고 시스템에 두 번째 명령 형식이 필요한 경우 이에 따라 명령을 조정해야 할 수도 있습니다 sort. 또 다른 함정은 일부 데이터 합산 프로그램이 파일 이름을 전혀 기록하지 않는다는 것입니다. 대표적인 예가 오래된 Unix sum프로그램입니다.

이 방법은 md5sumN+1번 호출하므로 다소 비효율적입니다. 여기서 N은 트리에 있는 파일 수이지만 파일 및 디렉터리 메타데이터 해싱을 피하기 위해 필요한 비용입니다.

옵션 2: 데이터 비교그리고메타데이터

이를 감지할 수 있어야 하는 경우아무것파일 내용뿐만 아니라 트리가 변경된 경우 tar디렉터리 내용을 압축해 달라고 요청한 후 다음 주소로 보내세요 md5sum.

$ tar -cf - somedir | md5sum

파일 권한, 소유권 등도 확인 하므로 tar파일 내용의 변경뿐만 아니라 해당 항목의 변경도 감지합니다.

이 방법은 트리를 한 번만 통과하고 해시 프로그램을 한 번만 실행하므로 상당히 빠릅니다.

find위의 기반 방법 과 마찬가지로 tar기본 파일 시스템이 파일을 반환하는 순서대로 파일 이름을 처리합니다. 귀하의 응용 프로그램에서 이러한 일이 발생하지 않을 것이라고 확신할 수도 있습니다. 나는 그런 경우가 발생할 가능성이 있는 적어도 세 가지의 서로 다른 사용 패턴을 생각할 수 있습니다. (우리는 지정되지 않은 동작 영역에 들어가고 있기 때문에 나열하지 않을 것입니다. 각 파일 시스템은 OS 버전마다 다를 수 있습니다.)

오탐지가 발생하는 경우 다음 find | cpio옵션을 사용하는 것이 좋습니다.질의 답변.

답변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두 부분을 분리합니다(이것이 없으면 이름이 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

두 디렉터리 간의 차이점을 찾는 것이 목표라면 diff 사용을 고려해 보세요.

이 시도:

diff -qr dir1 dir2

답변4

사용checksumdir:

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

더 빠르게그리고더 쉽게다른 bash 솔루션보다.

관련 정보