Come convincere tar (ecc.) Per archiviare i contenuti del dispositivo a blocchi?


13

Ho sei volumi logici Linux che supportano insieme una macchina virtuale. La VM è attualmente in fase di arresto, quindi è facile acquisirne immagini coerenti.

Vorrei mettere insieme tutte e sei le immagini in un archivio. In sostanza, potrei fare qualcosa del genere:

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

Ma questo ovviamente crea una copia extra. Vorrei evitare la copia extra.

L'approccio ovvio:

tar c /dev/Zia/vm_lvraid_* | whatever

non funziona, poiché tar riconosce i file come speciali (collegamenti simbolici in questo caso) e sostanzialmente li memorizza ln -snell'archivio. Oppure, con --dereferenceo direttamente indicato /dev/dm-X, li riconosce come speciali (file del dispositivo) e sostanzialmente li memorizza mknodnell'archivio.

Ho cercato le opzioni della riga di comando per tar per ignorare questo comportamento e non sono riuscito a trovarne. Ho anche provato cpio, stesso problema, e non sono riuscito a trovare alcuna opzione per sovrascriverlo lì. Ho anche provato 7z(idem). Lo stesso con pax. Ci ho anche provato zip, il che si è appena confuso.

modifica: guardando il codice sorgente di GNU tar e GNU cpio, sembra che nessuno dei due possa farlo. Almeno, non senza gravi inganni (la gestione speciale dei file del dispositivo non può essere disabilitata). Quindi, suggerimenti di inganno serio sarebbero apprezzati o utilità alternative.

TLDR: Esiste un programma di archiviazione che comprime più immagini del disco (prese da dispositivi non elaborati ) e trasmette in streaming l'output, senza effettuare ulteriori copie su disco? Le mie preferenze verrebbero emesse in un formato comune, come POSIX o GNU tar.


L'ho convinto.
Mikeserv,

Risposte:


11

Di recente ho voluto farlo con tar. Qualche indagine mi ha indicato che era più che un po 'assurdo che non potevo. Mi è venuta in mente questa split --filter="cat >file; tar -r ..."cosa strana , ma, beh, è ​​stato terribilmente lento. E più leggo tarpiù assurdità sembrava.

Vedete, tarè solo un elenco concatenato di record. I file costituenti non vengono modificati in alcun modo: sono interi all'interno dell'archivio. Ma sono bloccati su limiti di blocco da 512 byte e prima di ogni file c'è un'intestazione . Questo è tutto. Anche il formato dell'intestazione è davvero molto semplice.

Quindi, ho scritto il mio tar. Io lo chiamo ... 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"
}

Quella è la carne e le patate, davvero. Scrive le intestazioni e calcola il chksum - che, relativamente parlando, è l'unica parte difficile. Fa il ustarformato dell'intestazione ... forse . Almeno, emula ciò che GNU tarsembra pensare sia il ustarformato dell'intestazione al punto da non lamentarsi. E c'è di più, è solo che non l'ho ancora davvero coagulato . Qui, ti mostrerò:

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

Questo è tar. Tutto è riempito di \0null quindi mi trasformo emin \newline per leggibilità. 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 .

PRODUZIONE

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:.

Dico un po ' lassù perché quello non è lo shitarscopo - lo tarfa già magnificamente. Volevo solo mostrare come funziona, il che significa che devo toccare il chksum. Se non fosse stato per questo, mi limiterei a ddscrivere un tarfile e finirlo. A volte potrebbe anche funzionare, ma diventa disordinato quando ci sono più membri nell'archivio. Tuttavia, il chksum è davvero facile.

Per prima cosa, rendilo 7 spazi - (che è una cosa strana di gnu, penso, come dice la specifica 8, ma comunque - un hack è un hack) . Quindi sommare i valori ottali di ogni byte nell'intestazione. Questo è il tuo chksum. Quindi hai bisogno dei metadati del file prima di fare l'intestazione, o non hai un chksum. E questo è un ustararchivio, per lo più.

Ok. Ora, cosa si intende fare:

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

Ciò crea tre immagini del disco da 500 M, formatta e monta ciascuna e scrive un file su ciascuna.

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

Nota : apparentemente i dispositivi di blocco si bloccheranno sempre correttamente. Abbastanza utile.

Questo tarè il contenuto dei file del dispositivo disco in-stream e indirizza l'output a xz.

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

Ora, il momento della verità ...

 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

Evviva! Estrazione...

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

Confronto...

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

E il monte ...

sudo mount disk13 mnt
cat mnt/*
disk3 file3

E quindi, in questo caso, shitarfunziona bene, immagino. Preferirei non approfondire tutte le cose che non andranno bene. Ma, dirò: non fare almeno nuove righe nei nomi dei file.

Puoi anche fare - e forse dovresti, considerando le alternative che ho offerto - questo con squashfs. Non solo ottieni il singolo archivio creato dallo stream, ma è in mountgrado e integrato nei kernel vfs:

Da 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

Potresti anche usare btrfs (send|receive)per eseguire lo streaming di un volume secondario in qualunque stdincompressore adatto a te. Questo sottovolume non deve necessariamente esistere prima di decidere di usarlo come contenitore di compressione, ovviamente.

Tuttavia, circa squashfs...

Non credo di fare questa giustizia. Ecco un esempio molto semplice:

 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

Questo è solo l' -pargomento in linea per mksquash. Puoi creare un file -pfcontenente il numero di quelli che desideri. Il formato è semplice: definisci il nome / percorso di un file di destinazione nel filesystem del nuovo archivio, gli dai una modalità e un proprietario e poi gli dici da quale processo eseguire e leggere lo stdout. Puoi crearne quanti ne vuoi - e puoi usare LZMA, GZIP, LZ4, XZ ... hmm ci sono più ... formati di compressione che vuoi. E il risultato finale è un archivio in cui tu cd.

Altre informazioni sul formato:

Questo, ovviamente, non è solo un archivio: è un'immagine di file system Linux compresso e montabile. Il suo formato è quello del kernel Linux - è un filesystem supportato dal kernel vanilla. In questo modo è comune come il kernel Linux vaniglia. Quindi se mi dicessi che stavi eseguendo un sistema Linux vanilla su cui tarnon era installato il programma sarei dubbioso - ma probabilmente ti crederei. Ma se mi dicessi che stavi eseguendo un sistema Linux vanilla su cui il squashfsfilesystem non era supportato non ti avrei creduto.


Mike, potremmo aiutarti a creare un piccolo esempio autonomo in modo che le persone possano sperimentarlo? Sembra che tu possa fare almeno una parte di quello sopra, ma non ne sono sicuro. In input f 444 root root dd if=/dev/sda1 bs=1024 count=10è l'input del file? Forse sarebbe meglio creare un dispositivo giocattolo, riempirlo di dati e scrivere da esso? E tutto ciò richiede root?
Faheem Mitha,

@FaheemMitha - sì, posso farlo, ma non l'ho fatto qui. Il link è alla documentazione ufficiale - è presa direttamente da essa. Sarebbe meglio se avessi fatto un esempio di comando. L'ho già fatto in precedenza - è piuttosto bello. Comunque - il inputfile è un file squashfsnell'archivio - l'immagine del filesystem che risulta dall'esecuzione del comando. Quando lo fai mksquashpuoi specificare questi comandi pseudofile per i comandi che vengono eseguiti e da cui stdoutviene acquisito al momento della compressione.
Mikeserv,

@FaheemMitha - oh, e non richiede root per eseguire la compressione , anche se può fare il montaggio - è un'immagine del filesystem che ne risulta. È lo stesso filesystem che usano tutti i dischi di Linux Live. In effetti - una cosa molto interessante - è che puoi creare un'immagine di proprietà di root usando quegli pseudo-file senza essere root - come impostare i file del tuo dispositivo e i numeri MAJ: MIN arbitrari.
Mikeserv,

Immagino che dovrebbe essere possibile creare un file del dispositivo, scrivere su di esso e quindi da esso senza mai montarlo, giusto? Quindi, forse non richiede root, che sarebbe ovviamente preferibile.
Faheem Mitha,

Bene, non ci sono btrfs coinvolti qui, quindi non funzionerà. Ma squashfs è abbastanza pazzo che potrebbe funzionare. Sebbene abbia il rovescio della medaglia di non essere un formato di archivio comune.
derobert,

4

Il tuo problema mi ha lasciato perplesso per qualche tempo e penso di aver trovato una soluzione che avrebbe funzionato.

Penso che puoi ottenere quello che vuoi con 7z usando la -si{NAME}bandiera.

Sarai in grado di adattarti alle tue necessità.

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

EDIT : Rimuovi l'uso inutile di cat


Sarebbe utile avere un piccolo esempio che le persone possono provare. Ad esempio, creare un dispositivo a blocchi, scrivere su di esso, quindi scrivere da esso. Non richiedere root sarebbe un vantaggio.
Faheem Mitha,

Nell'esempio / dev / sda1 è un dispositivo a blocchi. Il comando cat ha lo scopo di scaricare il contenuto del dispositivo su stdout, quindi 7z crea (o aggiorna) l'archivio e memorizza i dati nel nome file specificato dal parametro -si da stdin. Il risultato all'interno dell'archivio è il contenuto di ciascun dispositivo a blocchi. Il comando "cat" ha bisogno di root per leggere i dati dal dispositivo.
Tony

Questo è un uso inutile di Cat , ma per il resto si adatta abbastanza bene al conto. Stranamente la mia 7zmanpage non menziona -si può prendere un nome file, ma funziona. Non è perfetto (l'output non può essere convogliato da qualche parte), ma è sicuramente il migliore finora in output in un formato comune.
derobert,

@FaheemMitha che richiede il root o no dipenderà dalle impostazioni dei permessi sul tuo sistema, sebbene solo il root possa creare nuovi dispositivi a blocchi.
derobert,

@derobert Rimosso il gatto :)
Tony
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.