Comment puis-je obtenir la somme MD5 du contenu d'un répertoire sous la forme d'une somme?


171

Le programme md5sum ne fournit pas de sommes de contrôle pour les répertoires. Je souhaite obtenir une somme de contrôle MD5 unique pour tout le contenu d'un répertoire, y compris les fichiers dans des sous-répertoires. C'est-à-dire qu'une somme de contrôle combinée est constituée de tous les fichiers. Y a-t-il un moyen de faire cela?

Réponses:


187

La bonne manière dépend exactement de la raison pour laquelle vous demandez:

Option 1: Comparer uniquement les données

Si vous avez juste besoin d'un hachage du contenu du fichier de l'arborescence, ceci fera l'affaire:

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

Ceci résume d'abord tout le contenu du fichier individuellement, dans un ordre prévisible, puis transmet cette liste de noms de fichiers et de hachages MD5, en donnant une valeur unique qui ne change que lorsque le contenu d'un des fichiers de l'arborescence est modifié.

Malheureusement, find -sfonctionne uniquement avec BSD find (1), utilisé dans macOS, FreeBSD, NetBSD et OpenBSD. Pour obtenir quelque chose de comparable sur un système avec GNU ou SUS find (1), vous avez besoin de quelque chose d'un peu plus laid:

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

Nous avons remplacé find -spar un appel à sort. Le -k 2bit lui dit de sauter le hachage MD5, donc il ne trie que les noms de fichiers, qui se trouvent dans les champs 2 à la fin de la ligne, en fonction sortdu compte rendu.

Cette version de la commande présente un point faible: elle risque de devenir confuse si vous avez des noms de fichiers contenant des nouvelles lignes, car cela ressemblera à plusieurs lignes à l' sortappel. La find -svariante n’a pas ce problème, car la traversée et le tri de l’arbre ont lieu dans le même programme find,.

Dans les deux cas, le tri est nécessaire pour éviter les faux positifs: les systèmes de fichiers Unix / Linux les plus courants ne conservent pas les listes de répertoires dans un ordre stable et prévisible. Vous pourriez ne pas vous en rendre compte en utilisant ls, etc., qui trient le contenu du répertoire pour vous. findsans -sou un sortappel imprimera les fichiers dans l'ordre de leur retour par le système de fichiers sous-jacent, ce qui donnera à cette commande une valeur de hachage modifiée si l'ordre des fichiers qui lui est donné en entrée change.

Vous devrez peut-être modifier les md5sumcommandes md5ou une autre fonction de hachage. Si vous choisissez une autre fonction de hachage et que vous avez besoin de la seconde forme de la commande pour votre système, vous devrez peut-être ajuster la sortcommande en conséquence. Un autre piège est que certains programmes de sommation de données n'écrivent pas du tout de nom de fichier, un bon exemple étant l'ancien sumprogramme Unix .

Cette méthode est quelque peu inefficace, appelant md5sumN + 1 fois, où N est le nombre de fichiers de l’arborescence, mais c’est un coût nécessaire pour éviter le hachage des métadonnées de fichiers et de répertoires.

Option 2: Comparer les données et les métadonnées

Si vous devez être en mesure de détecter que quelque chose dans une arborescence a changé, pas seulement le contenu du fichier, demandez tarde compacter le contenu du répertoire, puis envoyez-le à md5sum:

$ tar -cf - somedir | md5sum

Parce que tarvoit également les autorisations de fichiers, la propriété, etc., cela détectera également les modifications apportées à ces éléments, pas uniquement les modifications apportées au contenu du fichier.

Cette méthode est considérablement plus rapide, puisqu'elle ne fait qu'un seul passage sur l'arborescence et n'exécute le programme de hachage qu'une seule fois.

Comme avec la findméthode basée ci-dessus, tarva traiter les noms de fichiers dans l'ordre dans lequel le système de fichiers sous-jacent les renvoie. Il se peut que dans votre application, vous puissiez être sûr que cela ne se produise pas. Je peux penser à au moins trois modèles d'utilisation différents où cela est susceptible d'être le cas. (Je ne vais pas les énumérer, car nous entrons dans un territoire de comportement non spécifié. Chaque système de fichiers peut être différent ici, même d'une version du système d'exploitation à l'autre.)

Si vous trouvez des faux positifs, je vous conseillerais de find | cpiochoisir l' option dans la réponse de Gilles .


7
Je pense qu'il est préférable de naviguer dans le répertoire en cours de comparaison et d'utiliser à la find .place de find somedir. De cette façon, les noms de fichier sont les mêmes lorsque vous fournissez différentes spécifications de chemin à rechercher; cela peut être délicat :-)
Abbafei

Faut-il aussi trier les fichiers?
CMCDragonkai le

@CMCDragonkai: Que voulez-vous dire? Dans le premier cas, nous ne trier la liste des noms de fichiers. Dans le second cas, nous ne le faisons pas volontairement, car une partie de l' élément souligné dans la première phrase est que l'ordre des fichiers dans un répertoire a été modifié. Vous ne souhaitez donc rien trier.
Warren Young

@WarrenYoung Pouvez-vous expliquer un peu plus en détail pourquoi l'option 2 n'est pas toujours meilleure? Il semble être plus rapide, plus simple et plus multi-plateforme. Dans quel cas ne devrait-ce pas être l'option 1?
Robin Winslow

Option 1 alternative: find somedir -type f -exec sh -c "openssl dgst -sha1 -binary {} | xxd -p" \; | sort | openssl dgst -sha1ignorer tous les noms de fichiers (devrait fonctionner avec les nouvelles lignes)
windm

38

La somme de contrôle doit être une représentation déterministe et non ambiguë des fichiers sous forme de chaîne. Déterministe signifie que si vous placez les mêmes fichiers au même endroit, vous obtiendrez le même résultat. Univoque signifie que deux ensembles de fichiers différents ont des représentations différentes.

Données et métadonnées

Faire une archive contenant les fichiers est un bon début. C'est une représentation non ambiguë (évidemment, puisque vous pouvez récupérer les fichiers en extrayant l'archive). Il peut inclure des métadonnées de fichier telles que les dates et la propriété. Cependant, ceci n’est pas encore tout à fait correct: une archive est ambiguë, car sa représentation dépend de l’ordre dans lequel les fichiers sont stockés et, le cas échéant, de la compression.

Une solution consiste à trier les noms de fichiers avant de les archiver. Si vos noms de fichiers ne contiennent pas de saut de ligne, vous pouvez les find | sortrépertorier et les ajouter aux archives dans cet ordre. Prenez soin de dire à l’archiveur de ne pas récidiver dans les répertoires. Voici des exemples avec POSIX pax, GNU tar et 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

Noms et contenu uniquement, la méthode low-tech

Si vous souhaitez uniquement prendre en compte les données du fichier et non les métadonnées, vous pouvez créer une archive qui ne comprend que le contenu du fichier, mais il n'existe aucun outil standard pour cela. Au lieu d'inclure le contenu du fichier, vous pouvez inclure le hachage des fichiers. Si les noms de fichiers ne contiennent pas de saut de ligne et qu'il n'y a que des fichiers et des répertoires normaux (pas de liens symboliques ni de fichiers spéciaux), cela est assez simple, mais vous devez vous occuper de quelques points:

{ 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

Nous incluons une liste de répertoires en plus de la liste des sommes de contrôle, sinon des répertoires vides seraient invisibles. La liste de fichiers est triée (dans une langue spécifique et reproductible - merci à Peter.O de me l'avoir rappelé). echosépare les deux parties (sans cela, vous pourriez créer des répertoires vides dont le nom ressemble à une md5sumsortie et qui pourraient également passer pour des fichiers ordinaires). Nous incluons également une liste des tailles de fichiers, pour éviter les attaques par extension de longueur .

À propos, MD5 est obsolète. S'il est disponible, envisagez d'utiliser SHA-2 ou au moins SHA-1.

Noms et données, prenant en charge les nouvelles lignes dans les noms

Voici une variante du code ci-dessus qui repose sur les outils GNU pour séparer les noms de fichiers avec des octets nuls. Cela permet aux noms de fichiers de contenir des nouvelles lignes. Les utilitaires de résumé GNU citent des caractères spéciaux dans leur sortie, il n'y aura donc pas de saut de ligne ambigu.

{ 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

Une approche plus robuste

Voici un script Python minimalement testé qui crée un hachage décrivant une hiérarchie de fichiers. Il prend les répertoires et le contenu des fichiers dans les comptes, ignore les liens symboliques et les autres fichiers, et renvoie une erreur fatale si un fichier ne peut pas être lu.

#! /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()

OK, ça marche, merci. Mais existe-t-il un moyen de le faire sans inclure de métadonnées? En ce moment, j'en ai besoin uniquement pour le contenu actuel.

Que diriez-vous LC_ALL=C sortde vérifier de différents environnements ... (+ 1 btw)
Peter.O

Vous avez créé tout un programme Python pour cela? Merci! C'est vraiment plus que ce à quoi je m'attendais. :-) En tout cas, je vais vérifier ces méthodes ainsi que la nouvelle option 1 de Warren.

Bonne réponse. Définir l'ordre de tri avec LC_ALL=Cest essentiel si vous utilisez plusieurs ordinateurs et plusieurs systèmes d'exploitation.
Davor Cubranic

Qu'est-ce que ça cpio -o -veut dire? Cpio n'utilise-t-il pas stdin / out par défaut? GNU cpio 2.12 produitcpio: Too many arguments
Jan Tojnar

12

Jetez un coup d'œil à md5deep . Certaines des fonctionnalités de md5deep qui pourraient vous intéresser:

Opération récursive - md5deep peut examiner de manière récursive une arborescence de répertoires complète. En d’autres termes, calculez le MD5 pour chaque fichier d’un répertoire et pour chaque fichier de chaque sous-répertoire.

Mode comparaison - md5deep peut accepter une liste de hachages connus et les comparer à un ensemble de fichiers d’entrée. Le programme peut afficher les fichiers d’entrée qui correspondent à la liste des hachages connus ou ceux qui ne correspondent pas.

...


Bien, mais ça ne marche pas, dit-on .../foo: Is a directory, qu'est-ce qui donne?
Camilo Martin

3
À lui seul, md5deep ne résout pas le problème de l'OP car il n'imprime pas de somme consolidée md5, il n'imprime que la somme md5 pour chaque fichier du répertoire. Cela dit, vous pouvez md5sum la sortie de md5deep - pas tout à fait ce que voulait le PO, mais proche! Par exemple, pour le répertoire en cours: md5deep -r -l -j0 . | md5sum(où -rest récursif, -lsignifie "utiliser des chemins relatifs" afin que le chemin absolu des fichiers n'interfère pas lorsque vous essayez de comparer le contenu de deux répertoires, et -j0signifie utiliser 1 thread pour éviter le non-déterminisme dû les sommes individuelles md5 étant retournées dans des ordres différents).
Stevie

Comment ignorer certains fichiers / répertoires dans le chemin?
Sandeepan Nath

9

Si votre objectif est simplement de trouver des différences entre deux répertoires, envisagez d'utiliser diff.

Essaye ça:

diff -qr dir1 dir2

Oui, cela est utile aussi. Je pense que vous vouliez dire dir1 dir2 dans cette commande.

1
Je n'utilise généralement pas les interfaces graphiques lorsque je peux les éviter, mais pour les différences de répertoires, kdiff3 est génial et fonctionne également sur de nombreuses plates-formes.
Sinelaw

Des fichiers différents sont également signalés avec cette commande.
Serge Stroobandt

7

Vous pouvez hacher chaque fichier de manière récursive, puis le texte résultant:

> md5deep -r -l . | sort | md5sum
d43417958e47758c6405b5098f151074 *-

md5deep est requis.


1
au lieu d'être md5deeputilisé hashdeepsur ubuntu 16.04 car le paquet md5deep est juste un dummy de transition pour hashdeep.
Palik

1
J'ai essayé le hashdeep. Il génère non seulement des hachages, mais également certains en-têtes, y compris ## Invoked from: /home/myuser/dev/votre chemin actuel ## $ hashdeep -s -r -l ~/folder/. Cela doit être trié, donc le hash final sera différent si vous modifiez votre dossier actuel ou votre ligne de commande.
Truf

3

Contenu du fichier uniquement , à l'exclusion des noms de fichiers

J'avais besoin d'une version qui ne vérifiait que les noms de fichiers car le contenu se trouvait dans des répertoires différents.

Cette version (la réponse de Warren Young) a beaucoup aidé, mais ma version de md5sumsort le nom de fichier (par rapport au chemin depuis lequel la commande a été exécutée), et les noms de dossier étaient différents. Par conséquent, même si les sommes de contrôle du fichier individuel correspondent, 't.

Pour résoudre ce problème, dans mon cas, il me suffisait de supprimer le nom du fichier de chaque ligne de la findsortie (sélectionnez uniquement le premier mot séparé par des espaces cut):

find -s somedir -type f -exec md5sum {} \; | cut -d" " -f1 | md5sum

Vous devrez peut-être également trier les sommes de contrôle pour obtenir une liste reproductible.
eckes

3

solution :

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

fonctionne rapide et plus facile solution puis de script bash.

voir doc: https://pypi.python.org/pypi/checksumdir/1.0.5


si vous n'avez pas pip, vous devrez peut-être l'installer avec yum -y install python-pip (ou dnf / apt-get)
DmitrySemenov

3

nix-hashdepuis le gestionnaire de paquets Nix

La commande nix-hash calcule le hachage cryptographique du contenu de chaque chemin et l’imprime sur une sortie standard. Par défaut, il calcule un hachage MD5, mais d'autres algorithmes de hachage sont également disponibles. Le hachage est imprimé en hexadécimal.

Le hachage est calculé sur une sérialisation de chaque chemin: une sauvegarde de l'arborescence du système de fichiers enracinée dans le chemin. Cela permet de hacher les répertoires et les liens symboliques, ainsi que les fichiers normaux. Le dump est au format NAR produit par nix-store --dump. Ainsi, nix-hash path produit le même hachage cryptographique que nix-store --dump path | md5sum.


2

J'utilise cet extrait pour des volumes modérés :

find . -xdev -type f -print0 | LC_COLLATE=C sort -z | xargs -0 cat | md5sum -

et celui-ci pour XXXL :

find . -xdev -type f -print0 | LC_COLLATE=C sort -z | xargs -0 tail -qc100 | md5sum -


Que fait le -xdevdrapeau?
czerasz

Il vous est demandé de taper: man findet de lire ce manuel
détaillé

Bon point :-). -xdev Don't descend directories on other filesystems.
czerasz

1
Notez que cela ignore les nouveaux fichiers vides (comme si vous touchez un fichier).
RonJohn

Il existe de nombreux cas où cela produira le même md5sum avec une structure de fichier et de répertoire complètement différente. Renommer des fichiers et des répertoires ne changera pas du tout cela si cela ne change pas l'ordre de tri des fichiers. Donc, je ne recommanderais pas cette approche.
Hans-Peter Störr le

2

L'arbre-id de Git est une bonne somme de contrôle d'arbre.

Il n'y a malheureusement pas d'outil autonome disponible pour le faire (du moins je ne le sais pas), mais si vous avez Git à portée de main, vous pouvez simplement prétendre configurer un nouveau référentiel et ajouter les fichiers que vous souhaitez vérifier à l'index.

Cela vous permet de produire le hachage (reproductible) de l’arborescence - qui ne comprend que le contenu, les noms de fichiers et certains modes de fichiers réduits (exécutable).


2

Pour donner suite à cette excellente réponse , si vous souhaitez accélérer le calcul de la somme de contrôle pour un répertoire volumineux, essayez GNU Parallel :

find -s somedir -type f | parallel -k -n 100 md5 {} | md5

(Ceci utilise un Mac avec md5, remplacez si nécessaire.)

L’ -kindicateur est important car il indique que l’ordre paralleldoit être maintenu, sinon la somme globale peut changer d’exécution même si les fichiers sont tous identiques. -n 100dit d’exécuter chaque instance de md5100 arguments, c’est un paramètre que vous pouvez modifier pour obtenir le meilleur temps d’exécution. Voir aussi -Xdrapeau de parallel(bien que dans mon cas personnel cela ait causé une erreur.)


1

Un script bien testé qui prend en charge un certain nombre d'opérations, notamment la recherche de doublons, la comparaison de données et de métadonnées, l'affichage d'ajouts, de modifications et de suppressions, pourrait vous intéresser .

À l'heure actuelle, les empreintes digitales ne produisent pas une somme de contrôle unique pour un répertoire, mais un fichier de transcription qui inclut les sommes de contrôle pour tous les fichiers de ce répertoire.

fingerprint analyze

Cela générera index.fingerprintdans le répertoire actuel qui inclut les sommes de contrôle, les noms de fichiers et la taille des fichiers. Par défaut, il utilise les deux MD5et SHA1.256.

À l'avenir, j'espère ajouter un support pour les arbres Merkle dans Fingerprint, ce qui vous donnera une somme de contrôle de niveau supérieur. Pour le moment, vous devez conserver ce fichier pour procéder à la vérification.


1

Je ne voulais pas de nouveaux exécutables ni de solutions maladroites alors voici ce que je pense:

#!/bin/sh
# md5dir.sh by Camilo Martin, 2014-10-01.
# Give this a parameter and it will calculate an md5 of the directory's contents.
# It only takes into account file contents and paths relative to the directory's root.
# This means that two dirs with different names and locations can hash equally.

if [[ ! -d "$1" ]]; then
    echo "Usage: md5dir.sh <dir_name>"
    exit
fi

d="$(tr '\\' / <<< "$1" | tr -s / | sed 's-/$--')"
c=$((${#d} + 35))
find "$d" -type f -exec md5sum {} \; | cut -c 1-33,$c- | sort | md5sum | cut -c 1-32

0

Une approche robuste et propre

  • Tout d’abord, n’utilisez pas la mémoire disponible ! Hachez un fichier en morceaux plutôt que d'alimenter tout le fichier.
  • Différentes approches pour différents besoins / objectifs (tout ce qui suit ou choisissez ce qui s’applique):
    • Ne hachez que le nom de toutes les entrées de l'arborescence
    • Hachez le contenu du fichier de toutes les entrées (en laissant la méta comme, le numéro d'inode, ctime, atime, mtime, la taille, etc., vous voyez l'idée)
    • Pour un lien symbolique, son contenu est le nom du référent. Hachez ou choisissez de sauter
    • Suivre ou ne pas suivre (nom résolu) le lien symbolique tout en hachant le contenu de l'entrée
    • S'il s'agit d'un répertoire, son contenu ne sont que des entrées de répertoire. Lors de la traversée récursive, ils seront éventuellement hachés, mais les noms des entrées de répertoire de ce niveau doivent-ils être hachés pour baliser ce répertoire? Utile dans les cas d'utilisation où le hachage est requis pour identifier rapidement une modification sans avoir à parcourir en profondeur pour en hacher le contenu. Un exemple serait le nom d'un fichier qui change, mais le reste du contenu reste identique et ce sont tous des fichiers assez volumineux.
    • Traitez bien les gros fichiers (encore une fois, faites attention à la RAM)
    • Gérer les arborescences de répertoires très profondes (attention aux descripteurs de fichiers ouverts)
    • Gérer les noms de fichiers non standard
    • Comment procéder avec des fichiers qui sont des sockets, des pipes / FIFO, des périphériques blocs, des périphériques char? Faut-il les hacher aussi?
    • Ne mettez pas à jour le temps d'accès d'une entrée lors d'une traversée car ce sera un effet secondaire et contre-productif (intuitif?) Pour certains cas d'utilisation.

C’est ce que j’ai en tête: quiconque a passé du temps à travailler là-dessus aurait attrapé d’autres pièges et autres cas.

Voici un outil (disclaimer: j'y contribue) dtreetrawl , très léger sur la mémoire, ce qui, dans la plupart des cas, peut sembler un peu approximatif, mais a été très utile.

Usage:
  dtreetrawl [OPTION...] "/trawl/me" [path2,...]

Help Options:
  -h, --help                Show help options

Application Options:
  -t, --terse               Produce a terse output; parsable.
  -d, --delim=:             Character or string delimiter/separator for terse output(default ':')
  -l, --max-level=N         Do not traverse tree beyond N level(s)
  --hash                    Hash the files to produce checksums(default is MD5).
  -c, --checksum=md5        Valid hashing algorithms: md5, sha1, sha256, sha512.
  -s, --hash-symlink        Include symbolic links' referent name while calculating the root checksum
  -R, --only-root-hash      Output only the root hash. Blank line if --hash is not set
  -N, --no-name-hash        Exclude path name while calculating the root checksum
  -F, --no-content-hash     Do not hash the contents of the file

Un exemple de sortie conviviale:

...
... //clipped
...
/home/lab/linux-4.14-rc8/CREDITS
        Base name                    : CREDITS
        Level                        : 1
        Type                         : regular file
        Referent name                :
        File size                    : 98443 bytes
        I-node number                : 290850
        No. directory entries        : 0
        Permission (octal)           : 0644
        Link count                   : 1
        Ownership                    : UID=0, GID=0
        Preferred I/O block size     : 4096 bytes
        Blocks allocated             : 200
        Last status change           : Tue, 21 Nov 17 21:28:18 +0530
        Last file access             : Thu, 28 Dec 17 00:53:27 +0530
        Last file modification       : Tue, 21 Nov 17 21:28:18 +0530
        Hash                         : 9f0312d130016d103aa5fc9d16a2437e

Stats for /home/lab/linux-4.14-rc8:
        Elapsed time     : 1.305767 s
        Start time       : Sun, 07 Jan 18 03:42:39 +0530
        Root hash        : 434e93111ad6f9335bb4954bc8f4eca4
        Hash type        : md5
        Depth            : 8
        Total,
                size           : 66850916 bytes
                entries        : 12484
                directories    : 763
                regular files  : 11715
                symlinks       : 6
                block devices  : 0
                char devices   : 0
                sockets        : 0
                FIFOs/pipes    : 0

Les conseils généraux sont toujours les bienvenus, mais les meilleures réponses sont spécifiques et accompagnées du code, le cas échéant. Si vous avez déjà utilisé l’outil auquel vous faites référence, veuillez l’inclure.
bu5hman

@ bu5hman Bien sûr! Je n'étais pas assez à l'aise pour en dire plus (jubiler?) Sur son fonctionnement puisque je participe à son développement.
six-k

0

Faire individuellement pour tous les fichiers de chaque répertoire.

# Calculating
find dir1 | xargs md5sum > dir1.md5
find dir2 | xargs md5sum > dir2.md5
# Comparing (and showing the difference)
paste <(sort -k2 dir1.md5) <(sort -k2 dir2.md5) | awk '$1 != $3'

0

La migration vers le format d'archive POSIX affecte les sommes de contrôle basées sur GNU Tar

Cette réponse se veut une mise à jour supplémentaire de l'approche consistant à utiliser la sortie Tar pour hacher le contenu des répertoires, comme cela avait été proposé (entre autres) dans les excellentes réponses de Warren Young et Gilles il y a quelque temps.

Depuis lors, au moins openSUSE (depuis sa version 12.2) a changé son format GNU Tar par défaut du format "GNU tar 1.13.x" au format (légèrement) supérieur "Format POSIX 1003.1-2001 (pax)" . En amont également (parmi les développeurs de GNU Tar), ils discutent de la même migration, voir par exemple le dernier paragraphe de cette page du manuel de GNU Tar :

Le format par défaut pour GNU tar est défini au moment de la compilation. Vous pouvez le vérifier en exécutant tar --helpet en examinant les dernières lignes de sa sortie. En règle générale, GNU tar est configuré pour créer des archives au gnuformat. Cependant, les versions ultérieures passeront à posix.

(Cette page donne également un bon aperçu des différents formats d’archives disponibles avec GNU Tar.)

Dans notre cas, où nous tarifions le contenu du répertoire et hachaîmes le résultat, et sans prendre de mesures spécifiques, le passage du format GNU au format POSIX a les conséquences suivantes:

  • Malgré un contenu de répertoire identique, la somme de contrôle obtenue sera différente.

  • Malgré un contenu de répertoire identique, la somme de contrôle obtenue sera différente d'une exécution à l'autre si les en-têtes pax par défaut sont utilisés.

Ce dernier provient du fait que le format POSIX (pax) inclut des en-têtes de pax étendus qui sont déterminés par une chaîne de format par défaut %d/PaxHeaders.%p/%fdans GNU Tar. Dans cette chaîne, le spécificateur %pest remplacé par l'ID de processus du processus Tar générant, qui est bien sûr différent d'une exécution à l'autre. Voir cette section du manuel GNU Tar et en particulier celle-ci pour plus de détails.

En ce moment, datant du 2019-03-28, il existe un commit accepté en amont qui désamorce ce problème.

Donc, pour pouvoir continuer à utiliser GNU Tar dans le cas d'utilisation donné, je peux recommander les options alternatives suivantes:

  • Utilisez l'option Tar --format=gnupour indiquer explicitement à Tar de générer l'archive au format "ancien". Ceci est obligatoire pour valider les "anciennes" sommes de contrôle.

  • Utilisez le format POSIX plus récent, mais spécifiez explicitement un en-tête pax approprié, par exemple, par --pax-option="exthdr.name=%d/PaxHeaders/%f". Cependant, cela rompt la compatibilité avec les "anciennes" sommes de contrôle.

Voici un fragment de code Bash que j'utilise régulièrement pour calculer la somme de contrôle du contenu du répertoire, y compris les métadonnées:

( export LC_ALL=C
  find <paths> ! -type s -print0 |
  sort -z |
  tar cp --format=gnu --numeric-owner \
         --atime-preserve \
         --no-recursion --null --files-from - |
  md5sum --binary; )

Ici, <paths>est remplacée par une liste d'espaces des chemins de tous les répertoires que je veux voir couverts par la somme de contrôle. Le but d'utiliser les paramètres régionaux C, la séparation des noms de fichiers avec un octet nul et d'utiliser find et sort pour obtenir un ordre indépendant du système de fichiers des fichiers de l'archive est déjà suffisamment explicité dans les autres réponses.

Les parenthèses environnantes conservent le LC_ALLparamètre local dans un sous-shell.

De plus, j'utilise l'expression ! -type savec findpour éviter les avertissements de Tar qui se produisent si les fichiers de socket font partie du contenu du répertoire: GNU Tar n'archive pas les sockets. Si vous préférez être averti des sockets ignorés, laissez cette expression de côté.

J'utilise --numeric-owneravec Tar pour pouvoir vérifier les sommes de contrôle plus tard, même sur des systèmes où tous les propriétaires de fichiers ne sont pas connus.

L' --atime-preserveoption pour Tar est mieux omise si l'un des <paths>repose sur un périphérique monté en lecture seule. Sinon, vous serez averti pour chaque fichier dont l'horodatage d'accès n'a pas pu être restauré par Tar. Pour l'écriture activée <paths>, j'utilise cette option, eh bien, pour conserver les horodatages d'accès dans les répertoires hachés.

L’option Tar --no-recursion, qui était déjà utilisée dans la proposition de Gilles , empêche Tar de redescendre de manière récursive dans des répertoires et d’opérer à la place fichier par fichier sur tout ce qui est alimenté par la findsortie triée .

Et enfin, ce n’est pas vrai que j’utilise md5sum: j’utilise sha256sum.


-1

Si vous n’avez pas besoin de md5, vous pouvez essayer

find . -type f | xargs cksum | cksum

1
La question demande spécifiquement pour md5
RalfFriedl
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.