Encontrando arquivos esparsos?

19

Existe uma maneira simples de encontrar todos os arquivos esparsos no meu sistema ou em uma árvore de diretórios específica?

Se for relevante, estou usando zshno Ubuntu 12.04, embora uma resposta mais genérica do Unix-y para o bash / sh, por exemplo, esteja bem.

Editar : para esclarecer, estou procurando por arquivos esparsos, não para verificar o status de escassez de um único.

Andrew Ferrier
fonte
11
Relacionados: informações pormenorizadas arquivo esparso em Linux
Stéphane Chazelas
2
O que faz você se sentir em busca de arquivos esparsos não envolve a verificação do status de escassez de arquivos individuais?
Jlliagre

Respostas:

11

Em sistemas (e sistemas de arquivos) que suportam a SEEK_HOLE lseekflag (como o Ubuntu 12.04 no ext4 suportaria ) e assumindo que o valor para SEEK_HOLEé 4, como no Linux:

if perl -le 'seek STDIN,0,4;$p=tell STDIN;
   seek STDIN,0,2; exit 1 if $p == tell STDIN'< the-file; then
  echo the-file is sparse
else
  echo the-file is not sparse
fi

Essa sintaxe do shell é POSIX. O material não portátil nele é perle aquilo SEEK_HOLE.

lseek(SEEK_HOLE)procura o início do primeiro furo no arquivo ou o final do arquivo se nenhum buraco for encontrado. Acima, sabemos que o arquivo não é escasso quando nos lseek(SEEK_HOLE)leva ao final do arquivo (no mesmo local que lseek(SEEK_END)).

Se você deseja listar os arquivos esparsos:

find . -type f ! -size 0 -exec perl -le 'for(@ARGV){open(A,"<",$_)or
  next;seek A,0,4;$p=tell A;seek A,0,2;print if$p!=tell A;close A}' {} +

O GNU find(desde a versão 4.3.3) deve -printf %Srelatar a escassez de um arquivo. Ele segue a mesma abordagem da resposta de frostschutz, na medida em que leva a proporção de uso do disco e tamanho do arquivo, por isso não é garantido que todos os arquivos esparsos sejam reportados (como quando há compressão no nível do sistema de arquivos ou onde o espaço economizado pelos furos não é compensar a sobrecarga da infraestrutura do sistema de arquivos ou grandes atributos estendidos), mas funcionaria em sistemas que não possuem SEEK_HOLEou sistemas de arquivos onde SEEK_HOLEnão são implementados. Aqui com as ferramentas GNU:

find . -type f ! -size 0 -printf '%S:%p\0' |
  awk -v RS='\0' -F : '$1 < 1 {sub(/^[^:]*:/, ""); print}'

(observe que uma versão anterior desta resposta não funcionou corretamente quando findexpressa a escassez como, por exemplo, 3.2e-05. Obrigado à resposta do @ flashydave por chamar a atenção)

Stéphane Chazelas
fonte
O mesmo comentário acima; Estou procurando uma maneira de encontrar todos os arquivos esparsos, e não verificar um arquivo específico.
Andrew Ferrier 12/08
11
Talvez findtambém deva excluir arquivos de 0 byte diretamente?
Frostschutz 12/08
@frostschutz, bom ponto, resposta atualizada.
Stéphane Chazelas 12/08
Bom encontrar com o find -printf '%S'! :-)
frostschutz
11
@Brian, substitua o trcomando porxargs -r0 rm -f
Stéphane Chazelas
8

Um arquivo geralmente é escasso quando o número de blocos alocados é menor que o tamanho do arquivo (aqui, usando o GNU statcomo encontrado no Ubuntu, mas cuidado, outros sistemas podem ter implementações incompatíveis de stat).

if [ "$((`stat -c '%b*%B-%s' -- "$file"`))" -lt 0 ]
then
    echo "$file" is sparse
else
    echo "$file" is not sparse
fi

Variante com find: (roubado de Stephane)

find . -type f ! -size 0 -exec bash -c '
    for f do
        [ "$((`stat -c "%b*%B-%s" -- "$f"`))" -lt 0 ] && printf "%s\n" "$f";
    done' {} +

Você normalmente colocaria isso em um script de shell e, em seguida, executaria o script de shell.

find . -type f ! -size 0 -exec ./sparsetest.sh {} +
frostschutz
fonte
Isso pode não funcionar se os blocos esparsos não forem suficientes para cobrir a sobrecarga de blocos indiretos nos sistemas de arquivos tradicionais, por exemplo, ou se a compactação em vez da escassez estiver reduzindo a quantidade de espaço alocado.
Stéphane Chazelas
Certo; SEEK_HOLEé tão problemático, pois não é suportado por muitas plataformas / sistemas de arquivos. No Linux, você também pode usar FIEMAP/ FIBMAP, mas FIBMAPem particular é terrivelmente lento ... simplesmente não parece ser uma boa maneira.
Frostschutz
Muitos desses métodos também exigem que o arquivo seja sincronizado primeiro.
Frostschutz
Obrigado. Isso realmente não responde à pergunta, no entanto. Não estou olhando para verificar se um arquivo específico é escasso, mas para encontrar todos os arquivos esparsos no sistema.
Andrew Ferrier 12/08
11
@AndrewFerrier desculpe, acho que achei trivial o suficiente para envolver isso em um for file in *ou find. Se você pode testar um único arquivo, pode testar todos os arquivos ... embora seja necessário excluir diretórios com esse método.
Frostschutz
3

A resposta de Stephane Chazelas acima não leva em consideração o fato de que alguns arquivos esparsos com o parâmetro find% S relatam a razão como números de ponto flutuante, como

9.31323e-09:./somedir/sparsefile.bin

Estes podem ser encontrados em conjunto com

find . -type f ! -size 0 -printf '%S:%p\0' |
   sed -zn '/^\(0[^:]*:\)\|\([0-9.]\+e-.*:\)/p' |
   tr '\0' '\n'
flashydave
fonte
1

Um script curto que escrevi enquanto tentava descobrir quais são os locais dos furos em um arquivo:

#!/usr/bin/python3
import os
import sys
import errno

def report(fname):
    fd = os.open(fname, os.O_RDONLY)
    len = os.lseek(fd, 0, os.SEEK_END)
    offset = 0
    while offset < len:
        start = os.lseek(fd, offset, os.SEEK_HOLE)
        if start == len:
            break
        try:
            offset = os.lseek(fd, start, os.SEEK_DATA)
        except OSError as e:
            if e.errno == errno.ENXIO:
                offset = len
            else:
                raise
        print(f'found hole between 0x{start:08X} and 0x{offset:08X} ({offset - start} bytes)')

if __name__ == '__main__':
    for name in sys.argv[1:]:
        report(name)

Isso imprime coisas como:

$ echo -n 'a' >zeros; truncate -s $((4096*4)) zeros; test/report-holes.py zeros
found hole between 0x00001000 and 0x00004000 (12288 bytes)
zbyszek
fonte
Não responde à minha pergunta, pois estava procurando arquivos esparsos, não os buracos em um arquivo específico, mas ainda assim um script útil / relevante. Obrigado. Votado.
Andrew Ferrier