Jak przekonać tar (itp.) Do zarchiwizowania zawartości urządzenia blokowego?

13

Mam sześć woluminów logicznych systemu Linux, które razem tworzą maszynę wirtualną. Maszyna wirtualna jest obecnie wyłączona, więc łatwo jest robić spójne zdjęcia ich.

Chciałbym spakować wszystkie sześć zdjęć razem w archiwum. Po prostu mogłem zrobić coś takiego:

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

Ale to oczywiście tworzy dodatkową kopię. Chciałbym uniknąć dodatkowej kopii.

Oczywiste podejście:

tar c /dev/Zia/vm_lvraid_* | whatever

nie działa, ponieważ tar rozpoznaje pliki jako specjalne (w tym przypadku dowiązania symboliczne) i zasadniczo przechowuje je ln -sw archiwum. Lub, z --dereferencelub bezpośrednio wskazany /dev/dm-X, rozpoznaje je jako specjalne (pliki urządzeń) i zasadniczo przechowuje je mknodw archiwum.

Szukałem opcji wiersza polecenia do tar, aby zastąpić to zachowanie i nie znalazłem żadnego. Próbowałem również cpio, ten sam problem, i nie mogłem znaleźć żadnych opcji, aby go zastąpić. Też próbowałem 7z(ditto). To samo z pax. Próbowałem nawet zip, co się po prostu pogubiło.

edycja: Patrząc na kod źródłowy GNU tar i GNU cpio, wydaje się, że żaden z nich nie może tego zrobić. Przynajmniej nie bez poważnych sztuczek (nie można wyłączyć specjalnej obsługi plików urządzeń). Tak więc mile widziane byłyby sugestie poważnego oszustwa lub alternatywne narzędzia.

TLDR: Czy istnieje jakiś archiwizator, który spakuje wiele obrazów dysku razem (pobranych z surowych urządzeń) i przesyła strumieniowo to wyjście, bez wykonywania dodatkowych kopii na dysku? Moje preferencje byłyby wyprowadzane we wspólnym formacie, takim jak POSIX lub GNU tar.

derobert
źródło
Przekonałem to.
mikeserv

Odpowiedzi:

11

Ostatnio chciałem to zrobić tar. Niektóre dochodzenie wskazało mi, że nie było to trochę nonsensowne. Wymyśliłem tę dziwną split --filter="cat >file; tar -r ..."rzecz, ale, cóż, było to strasznie wolne. Im więcej czytałem o tartym, tym bardziej wydawało się to bezsensowne.

Widzisz, tarto tylko połączona lista rekordów. Pliki składowe nie są w żaden sposób zmieniane - są całe w archiwum. Są one jednak zablokowane na 512-bajtowych granicach bloku , a przed każdym plikiem znajduje się nagłówek . Otóż ​​to. Format nagłówka jest również bardzo prosty.

Więc napisałem własny tar. Ja to nazywam ... 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"
}

To naprawdę mięso i ziemniaki. Zapisuje nagłówki i oblicza chksum - co, relatywnie rzecz biorąc, jest jedyną trudną częścią. Robi ustarformat nagłówka ... może . Przynajmniej emuluje to, co GNU tarwydaje się uważać za ustarformat nagłówka do tego stopnia, że ​​nie narzeka. I jest coś więcej, po prostu jeszcze tego nie skoagulowałem . Tutaj pokażę ci:

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

To tar. Wszystko jest wypełnione \0zerami, więc zmieniam się emw \newline dla czytelności. I 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 .

WYNIK

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

Mówię trochę na górze, bo to nie shitarjest cel - tarrobi to już pięknie. Chciałem tylko pokazać, jak to działa - co oznacza, że ​​muszę dotknąć chksum. Gdyby nie to, po prostu ddoderwałbym się od tarpliku i skończyłbym z nim. Może to czasem działać, ale robi się bałagan, gdy w archiwum jest wielu członków. Mimo to, chksum jest naprawdę łatwe.

Najpierw zrób 7 spacji - (myślę, że to dziwna rzecz gnu, jak mówi specyfikacja 8, ale cokolwiek - hack to hack) . Następnie dodaj wartości ósemkowe każdego bajtu w nagłówku. To twoje chksum. Więc potrzebujesz metadanych pliku przed wykonaniem nagłówka, inaczej nie masz programu chksum. A to ustargłównie archiwum.

Dobrze. Co to znaczy:

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

To sprawia, że ​​trzy obrazy dysków 500M, formatują i montuje każdy, i zapisuje plik do każdego.

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

Uwaga - najwyraźniej urządzenia blokujące zawsze będą blokować poprawnie. Bardzo przydatny.

To tarjest zawartość plików urządzeń dyskowych typu In-Stream i potokuje dane wyjściowe 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

Chwila prawdy ...

 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

Brawo! Ekstrakcja...

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

Porównanie...

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

A wierzchowiec ...

sudo mount disk13 mnt
cat mnt/*
disk3 file3

I tak w tym przypadku shitardziała dobrze. Wolałbym nie zajmować się wszystkimi rzeczami, które nie przyniosą dobrego rezultatu. Ale powiem - nie rób co najmniej nowego wiersza w nazwach plików.

Możesz także zrobić - a może powinieneś, biorąc pod uwagę oferowane przeze mnie alternatywy - z tym squashfs. Nie tylko dostajesz jedno archiwum zbudowane ze strumienia - ale jest w mountstanie i wbudowane w jądro vfs:

Z pseudopliku. Przykład :

# 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

Możesz także użyć btrfs (send|receive)do przesłania strumienia objętości do dowolnego stdinkompresora, który ci się podoba. Ta podobjętość nie musi oczywiście istnieć, zanim zdecydujesz się użyć jej jako kontenera kompresyjnego.

Mimo to około squashfs...

Nie wierzę, że robię tę sprawiedliwość. Oto bardzo prosty przykład:

 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

To tylko wewnętrzny -pargument za mksquash. Możesz pobrać plik -pfzawierający tyle plików, ile chcesz. Format jest prosty - definiujesz nazwę / ścieżkę pliku docelowego w systemie plików nowego archiwum, nadajesz mu tryb i właściciela, a następnie mówisz, z którego procesu wykonać i odczytać standardowe wyjście. Możesz utworzyć tyle, ile chcesz - i możesz używać LZMA, GZIP, LZ4, XZ ... hmm jest więcej ... formatów kompresji, jak chcesz. A efektem końcowym jest archiwum, w którym ty cd.

Więcej o tym formacie:

Oczywiście nie jest to tylko archiwum - jest to skompresowany, możliwy do zamontowania obraz systemu plików Linux. Jego format to jądro Linuksa - jest to system plików obsługiwany przez jądro wanilii. W ten sposób jest tak powszechny jak waniliowe jądro Linuksa. Więc jeśli powiedziałbyś mi, że używasz waniliowego systemu Linux, na którym tarprogram nie został zainstalowany, byłbym wątpliwy - ale prawdopodobnie uwierzyłbym ci. Ale jeśli powiedziałbyś mi, że używasz waniliowego systemu Linux, na którym squashfssystem plików nie był obsługiwany, nie uwierzyłbym ci.

mikeserv
źródło
Mike, czy możemy sprawić ci kłopot w stworzeniu małego, samodzielnego przykładu, aby ludzie mogli z nim eksperymentować? Wygląda na to, że robisz przynajmniej część tego, ale nie jestem pewien. In input f 444 root root dd if=/dev/sda1 bs=1024 count=10to f plik wejściowy? Być może lepiej byłoby stworzyć zabawkowe urządzenie, wypełnić je danymi i napisać z niego? I czy to wszystko wymaga rootowania?
Faheem Mitha,
@FaheemMitha - tak, mogę to zrobić, ale nie zrobiłem tego tutaj. Link znajduje się w oficjalnej dokumentacji - pochodzi bezpośrednio z niego. Lepiej byśmy jednak zrobili przykład polecenia. Zrobiłem to już wcześniej - to całkiem fajne. W każdym razie - inputplik jest plikiem w squashfsarchiwum - obraz systemu plików, który wynika z uruchomienia polecenia. Gdy to zrobisz mksquash, możesz określić te pseudopliki poleceń dla poleceń, które są uruchamiane i z których stdoutsą przechwytywane w czasie kompresji.
mikeserv
@FaheemMitha - och, i nie wymaga rootowania do kompresji , chociaż może to zrobić montowanie - jest to obraz systemu plików, który powstaje. Jest to ten sam system plików, z którego korzystają wszystkie dyski Linux Live. W rzeczywistości - jedną bardzo fajną rzeczą jest to, że możesz utworzyć obraz będący własnością roota za pomocą tych pseudoplików bez konieczności bycia rootem - jak ustawienie plików urządzenia i dowolnych numerów MAJ: MIN.
mikeserv
Myślę, że powinno być możliwe utworzenie pliku urządzenia, napisanie do niego, a potem z niego bez montowania go, prawda? Może więc nie wymaga rootowania, co oczywiście byłoby lepsze.
Faheem Mitha,
Cóż, nie ma tu żadnych btrfs, więc to nie zadziała. Ale squashfs jest na tyle szalony, że może działać. Chociaż ma tę wadę, że nie jest powszechnym formatem archiwum.
derobert,
4

Problem przez jakiś czas mnie intrygował i myślę, że znalazłem rozwiązanie, które zadziałałoby.

Myślę, że możesz osiągnąć to, co chcesz dzięki 7z za pomocą -si{NAME}flagi.

Będziesz mógł dostosować się do swoich potrzeb.

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

EDYCJA : Usuń Bezużyteczne użycie cat

Tony
źródło
Przydałby się mały przykład, który ludzie mogą wypróbować. Na przykład stwórz urządzenie blokowe, napisz do niego, a następnie wypisz z niego. Nie wymaganie roota byłoby plusem.
Faheem Mitha,
W przykładzie / dev / sda1 jest urządzeniem blokowym. Komenda cat ma na celu zrzucenie zawartości urządzenia na standardowe wyjście. Następnie 7z utwórz (lub zaktualizuj) archiwum i zapisz dane w nazwie pliku określonej parametrem -si z standardowego wejścia. Wynik w archiwum stanowi zawartość każdego urządzenia blokowego. Polecenie „cat” wymaga roota, aby odczytać dane z urządzenia.
Tony,
To bezużyteczne użycie kota , ale poza tym całkiem dobrze pasuje do rachunku. Dziwnie, moja strona 7znie wspomina, że ​​-si może przyjąć nazwę pliku, ale działa. To nie jest idealne (wyjście nie może być gdzieś potokowane), ale jest zdecydowanie najlepsze, jak dotąd, wyjście w wspólnym formacie.
derobert,
@ FaheemMitha wymagający roota lub nie będzie zależeć od ustawień uprawnień w twoim systemie, chociaż tylko root może tworzyć nowe urządzenia blokowe.
derobert,
@derobert Usunięto kota :)
Tony,