En primer lugar, no estás solo en el enigma sobre este tipo de problemas.
Esto no solo se limita, tmpfs
sino que ha sido una preocupación citada con
NFSv4 .
Si una aplicación lee 'agujeros' en un archivo disperso, el sistema de archivos convierte los bloques vacíos en bloques "reales" llenos de ceros y los devuelve a la aplicación.
Cuando md5sum
intenta escanear un archivo, elige explícitamente hacerlo en
orden secuencial , lo que tiene mucho sentido en función de lo que md5sum intenta hacer.
Como existen fundamentalmente "agujeros" en el archivo, esta lectura secuencial va a (en algunas situaciones) causar una copia en la operación de escritura para completar el archivo. Esto luego entra en un problema más profundo sobre si es fallocate()
compatible o no con la implementación del sistema de archivos FALLOC_FL_PUNCH_HOLE
.
Afortunadamente, no solo es tmpfs
compatible con esto, sino que hay un mecanismo para "cavar" los agujeros de nuevo.
Usando la utilidad CLI fallocate
podemos detectar con éxito y volver a excavar estos agujeros.
Según man 1 fallocate
:
-d, --dig-holes
Detect and dig holes. This makes the file sparse in-place, without
using extra disk space. The minimum size of the hole depends on
filesystem I/O block size (usually 4096 bytes). Also, when using
this option, --keep-size is implied. If no range is specified by
--offset and --length, then the entire file is analyzed for holes.
You can think of this option as doing a "cp --sparse" and then
renaming the destination file to the original, without the need for
extra disk space.
See --punch-hole for a list of supported filesystems.
fallocate
Sin embargo, opera en el nivel de archivo y cuando se está ejecutando md5sum
contra un dispositivo de bloque (solicitando lecturas secuenciales) está tropezando con la brecha exacta entre cómo fallocate()
debería funcionar la llamada al sistema. Podemos ver esto en acción:
En acción, usando su ejemplo, vemos lo siguiente:
$ fs=$(mktemp -d)
$ echo ${fs}
/tmp/tmp.ONTGAS8L06
$ dd if=/dev/zero of=${fs}/sparse100M conv=sparse seek=$((100*2*1024-1)) count=1 2>/dev/null
$ echo "Before:" "$(ls ${fs}/sparse100M -s)"
Before: 0 /tmp/tmp.ONTGAS8L06/sparse100M
$ sudo losetup /dev/loop0 ${fs}/sparse100M
$ sudo md5sum /dev/loop0
2f282b84e7e608d5852449ed940bfc51 /dev/loop0
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 102400 /tmp/tmp.ONTGAS8L06/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 0 /tmp/tmp.ONTGAS8L06/sparse100M
Ahora ... eso responde a tu pregunta básica. Mi lema general es "ponte raro", así que profundicé más ...
$ fs=$(mktemp -d)
$ echo ${fs}
/tmp/tmp.ZcAxvW32GY
$ dd if=/dev/zero of=${fs}/sparse100M conv=sparse seek=$((100*2*1024-1)) count=1 2>/dev/null
$ echo "Before:" "$(ls ${fs}/sparse100M -s)"
Before: 0 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo losetup /dev/loop0 ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 1036 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51 /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 1036 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 520 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51 /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 520 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 516 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51 /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 512 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 0 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51 /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 0 /tmp/tmp.ZcAxvW32GY/sparse100M
Verá que el simple hecho de realizar los losetup
cambios cambia el tamaño del archivo disperso. Por lo tanto, esto se convierte en una combinación interesante de dónde tmpfs
, el mecanismo HOLE_PUNCH fallocate
y los dispositivos de bloque se cruzan.