web-dev-qa-db-fra.com

Linux: calculer un seul hachage pour un dossier et un contenu donnés?

Il doit sûrement y avoir un moyen de le faire facilement! 

J'ai essayé les applications en ligne de commande Linux telles que sha1sum et md5sum, mais elles semblent uniquement capables de calculer des hachages de fichiers individuels et de générer une liste de valeurs de hachage, une pour chaque fichier. 

Je dois générer un seul hachage pour tout le contenu d'un dossier (pas seulement les noms de fichiers).

Je voudrais faire quelque chose comme

sha1sum /folder/of/stuff > singlehashvalue

Éditer: pour clarifier, mes fichiers se trouvent à plusieurs niveaux dans une arborescence de répertoires, ils ne sont pas tous dans le même dossier racine.

63
Ben L

Un moyen possible serait:

 sha1sum chemin/vers/dossier/* | sha1sum 

S'il existe une arborescence de répertoires complète, vous feriez probablement mieux d'utiliser find et xargs. Une commande possible serait

 trouver chemin/vers/dossier -type f -print0 | trier -z | xargs -0 sha1sum | sha1sum 

Et enfin, si vous devez également prendre en compte les autorisations et les répertoires vides:

(find path/to/folder -type f -print0  | sort -z | xargs -0 sha1sum;
 find path/to/folder \( -type f -o -type d \) -print0 | sort -z | \
   xargs -0 stat -c '%n %a') \
| sha1sum

Les arguments de stat le feront imprimer le nom du fichier, suivi de ses autorisations octales. Les deux recherches s'exécutent l'une après l'autre, ce qui double le nombre d'entrées/sorties sur disque, la première détecte tous les noms de fichiers et contrôle le contenu, la seconde recherche tous les noms de fichiers et de répertoires, ainsi que le nom et le mode d'impression. La liste des "noms de fichiers et sommes de contrôle", suivie de "noms et répertoires avec autorisations" sera ensuite contrôlée, pour une somme de contrôle plus petite.

78
Vatine
  • Commit le répertoire à git, utilise le hash de commit. Voir metastore pour un moyen de contrôler également les autorisations.

  • Utilisez un outil de détection d’intrusion dans le système de fichiers tel que aide .

  • hacher une boule de tar du répertoire:

    tar cvf - /path/to/folder | sha1sum

  • Codez quelque chose vous-même, comme vatine's oneliner :

    find /path/to/folder -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum

16
David Schmitt

Vous pouvez faire tar -c /path/to/folder | sha1sum

8
S.Lott

Si vous voulez juste vérifier si quelque chose dans le dossier a changé, je recommanderais celui-ci:

ls -alR --full-time /folder/of/stuff | sha1sum

Il vous donnera simplement un hachage de la sortie ls, qui contient les dossiers, les sous-dossiers, leurs fichiers, leur horodatage, leur taille et leurs autorisations. À peu près tout ce dont vous auriez besoin pour déterminer si quelque chose a changé. 

Veuillez noter que cette commande ne générera pas de hachage pour chaque fichier, mais c’est pourquoi elle devrait être plus rapide que rechercher. 

5
Shumoapp

Si vous voulez juste hacher le contenu des fichiers, en ignorant les noms de fichiers, vous pouvez utiliser

cat $FILES | md5sum

Assurez-vous que les fichiers sont dans le même ordre lors du calcul du hachage:

cat $(echo $FILES | sort) | md5sum

Mais vous ne pouvez pas avoir de répertoires dans votre liste de fichiers.

3
unbeknown

Il y a un script python pour ça:

http://code.activestate.com/recipes/576973-getting-the-sha-1-or-md5-hash-of-a-directory/

Si vous modifiez les noms d'un fichier sans changer leur ordre alphabétique, le script de hachage ne le détectera pas. Toutefois, si vous modifiez l'ordre des fichiers ou le contenu de tout fichier, l'exécution du script vous donnera un hachage différent de celui utilisé auparavant.

2
Kingdon

Un autre outil pour y parvenir:

http://md5deep.sourceforge.net/

En tant que sons: comme md5sum mais aussi récursif, plus d'autres fonctionnalités.

1
Jack

J'acheminerais les résultats pour des fichiers individuels via sort (pour empêcher une simple réorganisation des fichiers afin de changer le hachage) dans md5sum ou sha1sum, selon votre choix.

1
Rafał Dowgird

J'ai écrit un script Groovy pour faire ceci:

import Java.security.MessageDigest

public static String generateDigest(File file, String digest, int paddedLength){
    MessageDigest md = MessageDigest.getInstance(digest)
    md.reset()
    def files = []
    def directories = []

    if(file.isDirectory()){
        file.eachFileRecurse(){sf ->
            if(sf.isFile()){
                files.add(sf)
            }
            else{
                directories.add(file.toURI().relativize(sf.toURI()).toString())
            }
        }
    }
    else if(file.isFile()){
        files.add(file)
    }

    files.sort({a, b -> return a.getAbsolutePath() <=> b.getAbsolutePath()})
    directories.sort()

    files.each(){f ->
        println file.toURI().relativize(f.toURI()).toString()
        f.withInputStream(){is ->
            byte[] buffer = new byte[8192]
            int read = 0
            while((read = is.read(buffer)) > 0){
                md.update(buffer, 0, read)
            }
        }
    }

    directories.each(){d ->
        println d
        md.update(d.getBytes())
    }

    byte[] digestBytes = md.digest()
    BigInteger bigInt = new BigInteger(1, digestBytes)
    return bigInt.toString(16).padLeft(paddedLength, '0')
}

println "\n${generateDigest(new File(args[0]), 'SHA-256', 64)}"

Vous pouvez personnaliser l'utilisation pour éviter d'imprimer chaque fichier, modifier le résumé du message, supprimer le hachage de répertoire, etc. http://www.nsrl.nist.gov/testdata/

gary-macbook:Scripts garypaduana$ groovy dirHash.groovy /Users/garypaduana/.config
.DS_Store
configstore/bower-github.yml
configstore/insight-bower.json
configstore/update-notifier-bower.json
filezilla/filezilla.xml
filezilla/layout.xml
filezilla/lockfile
filezilla/queue.sqlite3
filezilla/recentservers.xml
filezilla/sitemanager.xml
gtk-2.0/gtkfilechooser.ini
a/
configstore/
filezilla/
gtk-2.0/
lftp/
menus/
menus/applications-merged/

79de5e583734ca40ff651a3d9a54d106b52e94f1f8c2cd7133ca3bbddc0c6758
1
haventchecked

Une approche robuste et propre

  • Tout d’abord, n’exploitez 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 est indiqué ci-dessous 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. En les parcourant de manière 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 un changement 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 des 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 sur ce projet aurait été victime d’autres pièges et d’autres cas graves. 

Voici un outil , très léger sur la mémoire, qui répond à la plupart des cas, peut-être un peu approximatif, mais a été très utile.

Un exemple d'utilisation et de sortie de dtreetrawl.

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

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

Application Options:
  -t, --terse               Produce a terse output; parsable.
  -j, --json                Output as JSON
  -d, --delim=:             Character or string delimiter/separator for terse output(default ':')
  -l, --max-level=N         Do not traverse tree beyond N level(s)
  --hash                    Enable hashing(default is MD5).
  -c, --checksum=md5        Valid hashing algorithms: md5, sha1, sha256, sha512.
  -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
  -s, --hash-symlink        Include symbolic links' referent name while calculating the root checksum
  -e, --hash-dirent         Include hash of directory entries while calculating root checksum

Un extrait 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
0
six-k

Voici une variante simple et brève de Python 3 qui convient parfaitement aux fichiers de petite taille (une arborescence source, par exemple, où chaque fichier peut facilement tenir dans RAM), en ignorant les répertoires vides autres solutions:

import os, hashlib

def hash_for_directory(path, hashfunc=hashlib.sha1):                                                                                            
    filenames = sorted(os.path.join(dp, fn) for dp, _, fns in os.walk(path) for fn in fns)         
    index = '\n'.join('{}={}'.format(os.path.relpath(fn, path), hashfunc(open(fn, 'rb').read()).hexdigest()) for fn in filenames)               
    return hashfunc(index.encode('utf-8')).hexdigest()                          

Cela fonctionne comme ceci:

  1. Trouver tous les fichiers du répertoire de manière récursive et les trier par nom
  2. Calcule le hachage (valeur par défaut: SHA-1) de chaque fichier (lit le fichier entier en mémoire)
  3. Crée un index textuel avec les lignes "filename = hash"
  4. Encodez cet index dans une chaîne d'octets UTF-8 et hachez-le

Vous pouvez passer une fonction de hachage différente en tant que second paramètre si SHA-1 n'est pas votre tasse de thé.

0
Thomas Perl

Essayez de le faire en deux étapes:

  1. créer un fichier avec des hachages pour tous les fichiers d'un dossier
  2. hacher ce fichier

Ainsi:

# for FILE in `find /folder/of/stuff -type f | sort`; do sha1sum $FILE >> hashes; done
# sha1sum hashes

Ou tout faire à la fois:

# cat `find /folder/of/stuff -type f | sort` | sha1sum
0
Joao da Silva

sha1sum pour générer la liste des valeurs de hachage, puis sha1sum cette liste à nouveau, cela dépend de ce que vous voulez accomplir.

0
Ronny Vindenes

Je devais vérifier dans un répertoire entier les modifications de fichiers. 

Mais avec l'exclusion, les horodatages, la propriété de l'annuaire.

L'objectif est d'obtenir une somme identique n'importe où, si les fichiers sont identiques.

Y compris hébergé sur d'autres machines, indépendamment des fichiers ou de leur modification.

md5sum * | md5sum | cut -d' ' -f1

Il génère une liste de hachage par fichier, puis concatène ces hachages en un seul.

C'est beaucoup plus rapide que la méthode tar.

Pour une confidentialité plus forte dans nos hachages, nous pouvons utiliser sha512sum sur la même recette.

sha512sum * | sha512sum | cut -d' ' -f1

Les hachages sont également identiques partout où vous utilisez sha512sum mais il n’existe aucun moyen connu de l’inverser.

0
Cryptopat

S'il s'agit d'un référentiel git et que vous souhaitez ignorer tous les fichiers de .gitignore, vous pouvez utiliser ceci:

git ls-files <your_directory> | xargs sha256sum | cut -d" " -f1 | sha256sum | cut -d" " -f1

Cela fonctionne bien pour moi.

0
ndbroadbent