Como convencer o tar (etc.) para arquivar o conteúdo do dispositivo de bloco?

13

Eu tenho seis volumes lógicos do Linux que juntos retornam uma máquina virtual. A VM está atualmente desligada, por isso é fácil tirar imagens consistentes deles.

Gostaria de reunir todas as seis imagens em um arquivo. Trivialmente, eu poderia fazer algo assim:

cp /dev/Zia/vm_lvraid_* /tmp/somedir
tar c /tmp/somedir | whatever

Mas isso, é claro, cria uma cópia extra. Eu gostaria de evitar a cópia extra.

A abordagem óbvia:

tar c /dev/Zia/vm_lvraid_* | whatever

não funciona, já que o tar reconhece os arquivos como especiais (neste caso, links simbólicos) e basicamente armazena o ln -s no arquivo. Ou, com --dereference ou diretamente apontado para /dev/dm-X , ele os reconhece como especiais (arquivos de dispositivos) e basicamente armazena mknod no arquivo morto.

Eu procurei por opções de linha de comando para tar para substituir esse comportamento e não consegui encontrar nenhum. Eu também tentei cpio , mesmo problema, e não encontrei nenhuma opção para substituí-lo lá também. Eu também tentei 7z (idem). Mesmo com pax . Eu até tentei zip , que acabou se confundindo.

edit: Olhando para o código-fonte do GNU tar e do GNU cpio, parece que nenhum deles pode fazer isso. Pelo menos, não sem truques sérios (o manuseio especial de arquivos de dispositivos não pode ser desativado). Assim, sugestões de truques sérios seriam apreciadas ou utilidades alternativas.

TLDR: Existe algum arquivador que irá empacotar várias imagens de disco juntas (tiradas de dispositivos brutos) e transmitir essa saída, sem fazer cópias extras em disco? Minha preferência seria produzida em um formato comum, como POSIX ou GNU tar.

    
por derobert 19.08.2014 / 22:37

2 respostas

10

Então, recentemente, eu queria fazer isso com tar . Alguma investigação indicou-me que era mais do que um pouco absurdo que eu não pudesse. Eu criei essa estranha split --filter="cat >file; tar -r ..." , mas, bem, foi terrivelmente lenta. E quanto mais eu li sobre tar , mais sem sentido parecia.

Você vê, tar é apenas uma lista concatenada de registros. Os arquivos constituintes não são alterados de forma alguma - eles são inteiros dentro do arquivo. Mas eles estão bloqueados em limites de 512 bytes bloco , e precedendo cada arquivo existe um cabeçalho . É isso aí. O formato do cabeçalho é muito simples também.

Então, eu escrevi meu próprio tar . Eu chamo isso de ... shitar .

z() (IFS=0; printf '%.s\0' $(printf "%.$(($1-${#2}))d"))
chk() (IFS=${IFS#??}; set -f; set -- $(     
        printf "$(fmt)" "$n" "$@" '' "$un" "$gn"               
);  IFS=; a="$*"; printf %06o "$(($(
        while printf %d+ "'${a:?}"; do a=${a#?}; done 2>/dev/null
)0))")                                                                 
fmt() { printf '%s\'"${1:-n}" %s "${1:+$(z 99 "$n")}%07d" \
    %07o %07o %011o %011o "%-${1:-7}s" ' 0' "${1:+$(z 99)}ustar  " %s \
    "${1:+$(z 31 "$un")}%s"
}

Essa é a carne e as batatas, na verdade. Ele escreve os cabeçalhos e calcula o chksum - que, relativamente falando, é a única parte difícil. Ele faz o formato do cabeçalho ustar ... talvez . Pelo menos, ele emula o que o GNU tar parece achar o formato do cabeçalho ustar ao ponto de não reclamar. E tem mais, é que eu ainda não o coagulei ainda. Aqui, eu vou te mostrar:

for f in 1 2; do echo hey > file$f; done
{ tar -cf - file[123]; echo .; } | tr \0 \n | grep -b .

0:file1                      #filename - first 100 bytes
100:0000644                  #octal mode - next 8
108:0001750                  #octal uid,
116:0001750                  #gid - next 16
124:00000000004              #octal filesize - next 12
136:12401536267              #octal epoch mod time - next 12
148:012235                   #chksum - more on this
155: 0                       #file type - gnu is weird here - so is shitar
257:ustar                    #magic string - header type
265:mikeserv                 #owner
297:mikeserv                 #group - link name... others shitar doesnt do
512:hey                      #512-bytes - start of file   
1024:file2                   #512 more - start of header 2
1124:0000644
1132:0001750
1140:0001750
1148:00000000004
1160:12401536267
1172:012236
1179: 0
1281:ustar  
1289:mikeserv
1321:mikeserv
1536:hey
10240:.                     #default blocking factor 20 * 512

Isso é tar . Tudo está preenchido com em nulls, então eu apenas transformo \n em shitar ewlines para legibilidade. E shitar :

#the rest, kind of, calls z(), fmt(), chk() + gets $mdata and blocks w/ dd
for n in file[123]
do d=$n; un=$USER; gn=$(id --group --name)
   set -- $(stat --printf "%a\n%u\n%g\n%s\n%Y" "$n")
   printf "$(fmt 0)" "$n" "$@" "$(chk "$@")" "$un" "$gn"
   printf "$(z $((512-298)) "$gn")"; cat "$d"  
   printf "$(x=$(($4%512));z $(($4>512?($x>0?$x:512):512-$4)))"
done |
{ dd iflag=fullblock conv=sync bs=10240 2>/dev/null; echo .; } |
tr \0 \n | grep -b .

OUTPUT

0:file1                 #it's the same. I shortened it.
100:0000644             #but the whole first file is here
108:0001750
116:0001750
124:00000000004
136:12401536267
148:012235              #including its checksum
155: 0
257:ustar  
265:mikeserv
297:mikeserv
512:hey
1024:file2
...
1172:012236             #and file2s checksum
...
1536:hey
10240:.

Eu digo meio que porque não é o propósito do tar - chksum já faz isso lindamente. Eu só queria mostrar como funciona - o que significa que preciso tocar no dd . Se não fosse por isso eu seria apenas tar ing fora da cabeça de um arquivo ustar e feito com ele. Isso pode até funcionar algumas vezes, mas fica confuso quando há vários membros no arquivo. Ainda assim, o chksum é muito fácil.

Primeiro, crie 7 espaços - (o que é uma coisa estranha do gnu, eu acho, como diz a especificação 8, mas seja o que for - um hack é um hack) . Em seguida, some os valores octal de cada byte no cabeçalho. Esse é o seu chksum. Então você precisa dos metadados do arquivo antes de fazer o cabeçalho, ou você não tem um chksum. E esse é um arquivo tar , principalmente.

Ok. Agora, o que se pretende fazer:

cd /tmp; mkdir -p mnt     
for d in 1 2 3                                                
do  fallocate -l $((1024*1024*500)) disk$d
    lp=$(sudo losetup -f --show disk$d)
    sync
    sudo mkfs.vfat -n disk$d "$lp"
    sudo mount  "$lp" mnt
    echo disk$d file$d | sudo tee mnt/file$d
    sudo umount mnt
    sudo losetup -d "$lp"
done

Isso faz três imagens de disco de 500M, formata e monta cada uma delas e grava um arquivo em cada uma delas.

for n in disk[123]
do d=$(sudo losetup -f --show "$n")
   un=$USER; gn=$(id --group --name)
   set -- $(stat --printf "%a\n%u\n%g\n$(lsblk -bno SIZE "$d")\n%Y" "$n")
   printf "$(fmt 0)" "$n" "$@" "$(chk "$@")" "$un" "$gn"
   printf "$(z $((512-298)) "$gn")"
   sudo cat "$d"
   sudo losetup -d "$d"
done | 
dd iflag=fullblock conv=sync bs=10240 2>/dev/null |
xz >disks.tar.xz

Observação - aparentemente, os dispositivos de bloqueio sempre serão bloqueados corretamente. Muito útil.

Esse xz é o conteúdo dos arquivos do dispositivo de disco in-stream e canaliza a saída para shitar .

ls -l disk*
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk1
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk2
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk3
-rw-r--r-- 1 mikeserv mikeserv    229796 Sep  3 01:05 disks.tar.xz

Agora, o momento da verdade ...

 xz -d <./disks.tar.xz| tar -tvf -
-rw-r--r-- mikeserv/mikeserv 524288000 2014-09-03 01:01 disk1
-rw-r--r-- mikeserv/mikeserv 524288000 2014-09-03 01:01 disk2
-rw-r--r-- mikeserv/mikeserv 524288000 2014-09-03 01:01 disk3

Hooray! Extração ...

xz -d <./disks.tar.xz| tar -xf - --xform='s/[123]/1&/'  
ls -l disk*
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk1
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk11
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk12
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk13
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk2
-rw-r--r-- 1 mikeserv mikeserv 524288000 Sep  3 01:01 disk3
-rw-r--r-- 1 mikeserv mikeserv    229796 Sep  3 01:05 disks.tar.xz

Comparação ...

cmp disk1 disk11 && echo yay || echo shite
yay

E o monte ...

sudo mount disk13 mnt
cat mnt/*
disk3 file3

E, nesse caso, squashfs é bom, eu acho. Eu prefiro não entrar em todas as coisas que não vão fazer bem. Mas, eu vou dizer - não faça novas linhas nos nomes dos arquivos, pelo menos.

Você também pode fazer - e talvez deva, considerando as alternativas que eu ofereci - com mount . Não apenas você obtém o único arquivo criado a partir do fluxo - , mas é vfs able e incorporado ao btrfs (send|receive) :

do kernel

Em pseudo-file.example :

# Copy 10K from the device /dev/sda1 into the file input.  Ordinarily
# Mksquashfs given a device, fifo, or named socket will place that special file
# within the Squashfs filesystem, this allows input from these special
# files to be captured and placed in the Squashfs filesystem.
input f 444 root root dd if=/dev/sda1 bs=1024 count=10

# Creating a block or character device examples

# Create a character device "chr_dev" with major:minor 100:1 and
# a block device "blk_dev" with major:minor 200:200, both with root
# uid/gid and a mode of rw-rw-rw.
chr_dev c 666 root root 100 1
blk_dev b 666 0 0 200 200

Você também pode usar stdin para criar um subvolume em qualquer% compressorsquashfs -capable que você gostou. Este subvolume não precisa existir antes de você decidir usá-lo como contêiner de compactação, é claro.

Ainda, sobre -p ...

Eu não acredito que estou fazendo essa justiça. Aqui está um exemplo muito simples:

 cd /tmp; mkdir ./emptydir
 mksquashfs ./emptydir /tmp/tmp.sfs -p \
    'file f 644 mikeserv mikeserv echo "this is the contents of file"'                             

Parallel mksquashfs: Using 6 processors
Creating 4.0 filesystem on /tmp/tmp.sfs, block size 131072.
[==================================================================================|] 1/1 100%
Exportable Squashfs 4.0 filesystem, gzip compressed, data block size 131072
        compressed data, compressed metadata, compressed fragments,... 
###...
###AND SO ON
###...

echo '/tmp/tmp.sfs /tmp/imgmnt squashfs loop,defaults,user 0 0'|
    sudo tee -a /etc/fstab >/dev/null

mount ./tmp.sfs     
cd ./imgmnt
ls

total 1
-rw-r--r-- 1 mikeserv mikeserv 29 Aug 20 11:34 file

cat file

this is the contents of file

cd ..
umount ./imgmnt

Esse é apenas o argumento mksquash in-line para -pf . Você pode criar um arquivo com cd contendo quantos você desejar. O formato é simples - você define o nome / caminho de um arquivo de destino no sistema de arquivos do novo arquivo, dá a ele um modo e um proprietário e então informa qual processo executar e ler stdout. Você pode criar quantos quiser - e você pode usar LZMA, GZIP, LZ4, XZ ... hmm, há mais ... formatos de compressão que você quiser. E o resultado final é um arquivo no qual você tar .

Mais sobre o formato:

Isto, é claro, não é apenas um arquivo - é uma imagem de sistema de arquivos Linux montável e compactada. Seu formato é o kernel do Linux - é um sistema de arquivos suportado pelo kernel vanilla. Desta forma, é tão comum quanto o kernel Linux vanilla. Então, se você me disse que estava rodando um sistema Linux baunilha, no qual o programa squashfs não estava instalado, eu ficaria duvidoso - mas provavelmente acreditaria em você. Mas se você me disse que estava rodando um sistema Linux baunilha no qual o sistema de arquivos %code% não era suportado, eu não acreditaria em você.

    
por 20.08.2014 / 05:57
4

Seu problema me intrigou por algum tempo e acho que encontrei uma solução que funcionaria.

Acho que você pode conseguir o que deseja com 7z usando o sinal -si{NAME} .

Você será capaz de se adaptar à sua necessidade.

7z a test.7z -siSDA2.txt < /dev/sda1
7z a test.7z -siSDA2.txt < /dev/sda2

7z l test.7z 

7-Zip [64] 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18
p7zip Version 9.20 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,8 CPUs)

Listing archive: test.7z

--
Path = test.7z
Type = 7z
Method = LZMA
Solid = -
Blocks = 2
Physical Size = 1770
Headers Size = 162

   Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
2014-08-19 22:01:08 .....         6314          804  SDA1.txt
2014-08-19 22:01:11 .....         6314          804  SDA2.txt
------------------- ----- ------------ ------------  ------------------------
                                 12628         1608  2 files, 0 folders

EDITAR : Remova o uso inútil do gato

    
por 20.08.2014 / 04:03