Primeiro, você não está sozinho na intrigante sobre esse tipo de problema.
Isso não se limita apenas a tmpfs
, mas tem sido uma preocupação citada com
NFSv4 .
If an application reads 'holes' in a sparse file, the file system converts empty blocks into "real" blocks filled with zeros, and returns them to the application.
Quando md5sum
está tentando verificar um arquivo, ele explicitamente escolhe fazer isso
ordem sequencial , que faz muito sentido com base no que o md5sum é
tentando fazer.
Como existem fundamentalmente "buracos" no arquivo, essa leitura sequencial vai
para (em algumas situações) causar uma cópia em escrever como operação para preencher o arquivo. Isso então fica
em uma questão mais profunda em torno de se fallocate()
, como implementado no
o sistema de arquivos suporta FALLOC_FL_PUNCH_HOLE
.
Felizmente, o tmpfs
não apenas suporta isso, mas há um mecanismo para
"cavar" os buracos de volta para fora.
Usando o utilitário CLI fallocate
, podemos detectar e reimprimir com êxito esses
furos.
Como por 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
opera no nível arquivo e quando você está executando md5sum
contra um dispositivo de bloco (solicitando leituras sequenciais) você está tropeçando no
lacuna exata entre como o fallocate()
syscall deve operar. Nós podemos ver isso
em ação:
Em ação, usando seu exemplo, vemos o seguinte:
$ 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
Agora ... isso responde à sua pergunta básica. Meu lema geral é "ficar estranho", então eu cavou ainda mais ...
$ 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
Você vê que apenas o ato de executar o losetup
altera o tamanho de
o arquivo esparso. Então, isso se torna uma combinação interessante de onde tmpfs
,
o mecanismo HOLE_PUNCH, fallocate
e dispositivos de bloco se cruzam.