¿Existe una herramienta como diff -r que compara un árbol de directorios con un archivo de manifiesto utilizando hashes?

¿Existe una herramienta como diff -r que compara un árbol de directorios con un archivo de manifiesto utilizando hashes?

Aquí está mi situación. Tengo dos volúmenes de archivos de almacenamiento en frío que (deberían) contener conjuntos de datos idénticos. Estos volúmenes contienen copias de seguridad a las que se accede con poca frecuencia. Me preocupa que, eventualmente, bitrot llegue a uno o ambos y corrompa sutilmente los datos contenidos en ellos. Sé que puedo buscar diff -ren los dos volúmenes y encontrar archivos que han cambiado o desaparecido entre los dos, pero no obtengo ninguna indicación útil sobre qué volumen tiene la copia "buena". Estos son discos USB, y convertirlos a algo como ZFS parece... oneroso.

Lo que me gustaría es una herramienta que recorra de forma recursiva el árbol de directorios y escriba un archivo de manifiesto que contenga la ruta y el nombre del archivo junto con un hash del contenido del archivo. Ejecutaría esta herramienta inmediatamente después de escribir los datos en cada volumen y almacenaría el archivo de manifiesto resultante en un almacenamiento cálido, tal vez bajo algún tipo de control de revisión.

Desde este archivo me gustaría poder ejecutar algo que funcione exactamente igual diff -r: me diría si se agregaron, eliminaron archivos o si se modificó su contenido. Solo que en lugar de comparar un volumen con otro, compararía un volumen con el archivo de manifiesto en buen estado. Usando este método, debería poder saber si los datos que estoy leyendo del disco dentro de meses/años son idénticos a los datos que puse originalmente en él.

Tendría que pensar que algo como esto ya existe. Puedo obtener algo parecido a un archivo de manifiesto usando:

find /mnt/my-volume -type f -exec md5sum {} + > manifest.txt

pero hasta ahora no he encontrado una buena manera de analizar este archivo y verificar cada hash de forma recursiva. Además, algo menos importante, esto no me dirá si apareció o desapareció un directorio vacío. (No se me ocurre por qué importaría, pero sería bueno saber que ocurrió).

¿Estoy en el camino correcto con esto o existe una herramienta más adecuada que pueda hacer este tipo de cosas?

Respuesta1

Tienes razón, esa herramienta ya existe. Si bien veo que su publicación está etiquetada como 'linux', tal vez una solución orientada a BSD sea edificante.

Utilidad mtree(8) de FreeBSDpuede hacer exactamente lo que estás pidiendo.

Suponer:

$ find .
.
./c
./c/file3
./b
./b/file2
./a
./a/file1

Para crear un manifiesto de esa jerarquía de archivos, incluido un hash sha256 de cada archivo, se haría lo siguiente:

$ mtree -c -K sha256 > /tmp/manifest.txt
$ cat /tmp/manifest.txt
#          user: diego
#       machine: myhost.example.com
#          tree: /data/home/diego/foo
#          date: Wed Mar 28 10:31:17 2018

# .
/set type=file uid=1001 gid=1001 mode=0710 nlink=1 flags=uarch
.               type=dir nlink=5 time=1522257963.738221000

# ./a
/set type=file uid=1001 gid=1001 mode=0600 nlink=1 flags=uarch
a               type=dir mode=0710 nlink=2 time=1522257932.680802000
    file1       size=29 time=1522257932.682389000 \
                sha256digest=6b4114c4f12e63c0ca44073de5ca0a2b39fedaceaa533af3dfdc89f00039c973
# ./a
..


# ./b
b               type=dir mode=0710 nlink=2 time=1522257937.929131000
    file2       size=29 time=1522257937.930666000 \
                sha256digest=9f7a0a49475bb6f98e609a4e057f0bc702c5e4706be5bd656a676fd8d15da7ef
# ./b
..


# ./c
c               type=dir mode=0710 nlink=2 time=1522257942.064315000
    file3       size=29 time=1522257942.065882000 \
                sha256digest=bd617f47217ef0605d3aff036778d10bf18cb2f415c45e8e362e2c091df19491
# ./c
..

Luego se puede verificar la jerarquía de archivos con el manifiesto canalizando el manifiesto a mtree:

$ mtree < /tmp/manifest.txt || echo fail

Los archivos agregados, eliminados, renombrados o modificados harán que la verificación falle:

$ touch foo
$ mtree < /tmp/manifest.txt || echo fail
.:      modification time (Wed Mar 28 10:34:56 2018, Wed Mar 28 10:37:01 2018)
extra: foo
fail
$ rm foo; touch b/file2; mtree < /tmp/manifest.txt || echo fail
.:      modification time (Wed Mar 28 10:34:56 2018, Wed Mar 28 10:39:39 2018)
b/file2: 
        modification time (Wed Mar 28 10:25:37 2018, Wed Mar 28 10:39:39 2018)
fail
$ mv c/file3 c/FILE3; rm a/file1; date >> b/file2; mtree < /tmp/manifest.txt || echo fail
.:      modification time (Wed Mar 28 10:34:56 2018, Wed Mar 28 10:39:39 2018)
c:      modification time (Wed Mar 28 10:25:42 2018, Wed Mar 28 10:41:59 2018)
extra: c/FILE3
b/file2:
        size (29, 58)
        modification time (Wed Mar 28 10:25:37 2018, Wed Mar 28 10:47:31 2018)
        sha256digest (0x9f7a0a49475bb6f98e609a4e057f0bc702c5e4706be5bd656a676fd8d15da7ef, 0x569c17bd1a1ca2447fd8167f103531bf3a7b7b4268f0f68b18506e586e7eea94)
a:      modification time (Wed Mar 28 10:25:32 2018, Wed Mar 28 10:41:59 2018)
./a/file1 missing
./c/file3 missing
fail

Respuesta2

Respetarían md5sum -c manifest.txtlas rutas almacenadas en manifest.txt. El findprograma sustituye {}la ruta completa al archivo encontrado, incluida cualquier ubicación de búsqueda especificada en la findlínea de comando, es decir, ./a/b/c/d/esustituirá el archivo ./a/b/c/d/epor el mismo comando. find ./a -type f -exec md5sum {} \;

El posible problema son las rutas absolutas, por lo que el 'comando de creación de manifiesto' más apropiado es:

cd /mnt/my-volume; find  -type f -exec md5sum {} + > manifest.txt

sin embargo, siempre puedes arreglar las rutas seddentro de mainfest.txt

información relacionada