Você está correto em sua suposição de que, enquanto todas as entradas de diretório são excluídas imediatamente após chamar unlink (), os blocos reais que compõem fisicamente o arquivo são apagados apenas disco quando nada está usando o inode mais. (Eu digo "diretório entradas " porque em vfat, um arquivo pode ter vários deles, por causa de como o suporte a nome de arquivo longo do vfat é implementado).
Neste contexto, por inode , refiro-me à estrutura na memória que o kernel do Linux usa para manipular arquivos. Ele é usado mesmo quando o sistema de arquivos não é "baseado em inode". No caso do vfat, o inode é simplesmente apoiado por alguns blocos no disco.
Observando o código-fonte do kernel do Linux, vemos que vfat_unlink
, que implementa a chamada do sistema unlink()
para vfat, faz aproximadamente o seguinte (extremamente simplificado para ilustração):
static int vfat_unlink(struct inode *dir, struct dentry *dentry)
{
fat_remove_entries(dir, &sinfo);
clear_nlink(inode);
}
Então o que acontece é:
-
fat_remove_entries
simplesmente remove a entrada do arquivo em seu diretório. -
clear_nlink
define a contagem de links para o inode como0
, o que significa que nenhum arquivo (ou seja, nenhuma entrada de diretório) aponta para esse inode mais.
Note que neste ponto, nem o inode nem sua representação física são tocados de qualquer forma (exceto pela diminuição da contagem de links), então ainda existe alegremente na memória e no disco, como se nada tivesse acontecido!
(A propósito, também é interessante notar que vfat_unlink
sempre define a contagem de links para 0
ao invés de apenas decrementá-los usando drop_link
. Isso deve indicar que sistemas de arquivos FAT não suportam hard links! E é mais uma indicação de que o próprio FAT não conhece nenhum conceito inode separado.)
Agora vamos dar uma olhada no que acontece quando o inode é despejado . evict_inode
é chamado quando não queremos mais o inode na memória. No início, isso pode acontecer apenas quando nenhum processo contém qualquer descritor de arquivo aberto para esse inode (mas, em teoria, isso também pode acontecer em um momento posterior). A implementação do FAT para evict_inode
parece (novamente simplificada) assim:
static void fat_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
if (!inode->i_nlink) {
inode->i_size = 0;
fat_truncate_blocks(inode, 0);
}
invalidate_inode_buffers(inode);
clear_inode(inode);
}
A mágica acontece exatamente dentro da if
-clause: se a contagem de links do inode for 0, significa que nenhuma entrada de diretório está realmente apontando para ela. Por isso, definimos seu tamanho para 0 e, na verdade, o truncamos para 0 bytes, o que, na verdade, o exclui do disco, limpando os blocos dos quais foi feito.
Assim, a corrupção que você está experimentando em seus experimentos é facilmente explicada: assim como você suspeitava, a entrada de diretório já havia sido removida (por vfat_unlink
), mas como o inode ainda não foi removido, os blocos foram ainda intocado, e ainda estavam marcados no FAT (um acrônimo para File Allocation Table) como usado. No entanto, o fsck.vfat
detecta que não existe mais nenhuma entrada de diretório que aponte para esses blocos, queixe e repare.
A propósito, CHKDSK
não apenas limparia esses blocos marcando-os como livres, mas criaria novos arquivos no diretório raiz apontando para o primeiro bloco em cada cadeia, com nomes como FILE0001.CHK
.