Bagaimana meyakinkan tar (dll.) Untuk mengarsipkan blokir konten perangkat?

13

Saya memiliki enam volume logis Linux yang bersama-sama mendukung mesin virtual. VM saat ini sedang dimatikan, sehingga mudah untuk mengambil gambar yang konsisten.

Saya ingin mengemas semua enam gambar bersama dalam arsip. Secara sepele, saya bisa melakukan sesuatu seperti ini:

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

Tapi itu tentu saja membuat salinan tambahan. Saya ingin menghindari salinan tambahan.

Pendekatan yang jelas:

tar c /dev/Zia/vm_lvraid_* | whatever

tidak berfungsi, karena tar mengenali file-file khusus (symlinks dalam hal ini) dan pada dasarnya menyimpan ln -sdalam arsip. Atau, dengan --dereferenceatau langsung menunjuk /dev/dm-X, itu mengenali mereka sebagai khusus (file perangkat) dan pada dasarnya menyimpan mknoddalam arsip.

Saya telah mencari opsi baris perintah untuk tar untuk menimpa perilaku ini, dan tidak dapat menemukannya. Saya juga mencoba cpio, masalah yang sama, dan tidak dapat menemukan opsi untuk menimpanya di sana, baik. Saya juga mencoba 7z(ditto). Sama dengan pax. Saya bahkan mencoba zip, yang justru membuat dirinya bingung.

sunting: Melihat kode sumber GNU tar dan GNU cpio, tampaknya tidak ada yang bisa melakukan ini. Setidaknya, bukan tanpa tipu daya serius (penanganan khusus file perangkat tidak dapat dinonaktifkan). Jadi, saran tipuan serius akan dihargai atau utilitas alternatif.

TLDR: Apakah ada beberapa pengarsip yang akan mengemas beberapa gambar disk bersama-sama (diambil dari perangkat mentah) dan mengalirkan output itu, tanpa membuat salinan di-disk tambahan? Preferensi saya adalah keluaran dalam format umum, seperti POSIX atau GNU tar.

derobert
sumber
Saya meyakinkan itu.
mikeserv

Jawaban:

11

Jadi baru-baru ini saya ingin melakukan ini tar. Beberapa investigasi menunjukkan kepada saya bahwa saya tidak bisa melakukannya. Aku memang menemukan hal aneh ini split --filter="cat >file; tar -r ...", tapi, yah, itu sangat lambat. Dan semakin saya membaca tentang tarsemakin tidak masuk akal rasanya.

Anda lihat, tarini hanya daftar catatan yang digabungkan. File konstituen tidak diubah dengan cara apa pun - mereka utuh dalam arsip. Tapi mereka diblokir pada batas blok 512-byte , dan sebelum setiap file ada header . Itu dia. Format tajuk juga sangat, sangat sederhana.

Jadi, saya menulis sendiri tar. Saya menyebutnya ... 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"
}

Itu daging dan kentang, sungguh. Ini menulis header dan menghitung chksum - yang, secara relatif, adalah satu-satunya bagian yang sulit. Itu ustarformat header ... mungkin . Paling tidak, ia meniru apa yang tartampaknya GNU anggap ustarformat tajuk ke titik yang tidak dikeluhkan. Dan ada lebih dari itu, hanya saja saya belum benar-benar menggumpalnya . Di sini, saya akan menunjukkan kepada Anda:

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

Itu tar. Semuanya diisi dengan \0nol jadi saya hanya berubah emmenjadi \newlines untuk dibaca. Dan 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 .

KELUARAN

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

Saya katakan agak di atas sana karena itu bukan shitartujuan - tarsudah melakukannya dengan indah. Saya hanya ingin menunjukkan cara kerjanya - yang berarti saya perlu menyentuh chksum. Kalau bukan karena itu saya hanya akan ddmengambil kepala tarfile dan selesai dengan itu. Itu bahkan kadang-kadang bisa berfungsi, tetapi akan berantakan ketika ada banyak anggota dalam arsip. Tetap saja, chksum sangat mudah.

Pertama, buat 7 spasi - (yang merupakan hal aneh gnu, saya pikir, seperti yang dikatakan spek 8, tapi apa pun - peretasan adalah peretasan) . Kemudian tambahkan nilai oktal dari setiap byte di header. Itu chksum Anda. Jadi Anda perlu metadata file sebelum Anda melakukan header, atau Anda tidak memiliki chksum. Dan itu ustarsebagian besar arsip.

Baik. Sekarang, apa yang harus dilakukan:

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

Itu membuat tiga gambar disk 500 juta, masing-masing format dan mount, dan menulis file untuk masing-masing.

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

Catatan - tampaknya memblokir perangkat hanya akan selalu memblokir dengan benar. Cukup berguna.

Itulah tarisi file perangkat disk yang di-stream dan mengirim output ke pip 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

Sekarang, momen kebenaran ...

 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

Hore! Ekstraksi ...

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

Perbandingan...

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

Dan gunung ...

sudo mount disk13 mnt
cat mnt/*
disk3 file3

Jadi, dalam hal ini, shitarberkinerja baik, kurasa. Saya lebih suka tidak membahas semua hal yang tidak akan berhasil dengan baik. Tapi, saya akan mengatakan - jangan lakukan baris baru di nama file setidaknya.

Anda juga dapat melakukan - dan mungkin harus, mempertimbangkan alternatif yang saya tawarkan -ini squashfs. Anda tidak hanya mendapatkan arsip tunggal yang dibangun dari aliran - tetapi juga mountdapat dan dibangun di dalam kernel vfs:

Dari 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

Anda juga dapat menggunakan btrfs (send|receive)untuk mengalirkan subvolume ke dalam stdinkompresor apa pun yang Anda inginkan. Subvolume ini tidak perlu ada sebelum Anda memutuskan untuk menggunakannya sebagai wadah kompresi, tentu saja.

Namun, tentang squashfs...

Saya tidak percaya saya melakukan keadilan ini. Berikut ini contoh yang sangat sederhana:

 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

Itu hanya -pargumen inline untuk mksquash. Anda dapat sumber file dengan yang -pfmengandung sebanyak yang Anda suka. Formatnya sederhana - Anda menentukan nama / jalur file target di sistem file arsip baru, Anda memberikannya mode dan pemilik, dan kemudian Anda memberi tahu proses untuk mengeksekusi dan membaca stdout dari. Anda dapat membuat sebanyak yang Anda suka - dan Anda dapat menggunakan LZMA, GZIP, LZ4, XZ ... hmm ada lebih banyak ... format kompresi yang Anda inginkan. Dan hasil akhirnya adalah arsip tempat Anda cd.

Lebih lanjut tentang format:

Ini, tentu saja, bukan hanya arsip - ini adalah citra sistem file Linux yang dapat dimounting. Formatnya adalah kernel Linux - itu adalah sistem file yang didukung oleh vanilla kernel. Dengan cara ini, ini sama dengan kernel vanilla Linux. Jadi jika Anda mengatakan kepada saya Anda menjalankan sistem Linux vanilla di mana tarprogram tidak diinstal saya akan meragukan - tapi saya mungkin akan percaya Anda. Tetapi jika Anda mengatakan kepada saya Anda menjalankan sistem Linux vanilla di mana squashfsfilesystem tidak didukung saya tidak akan percaya Anda.

mikeserv
sumber
Mike, dapatkah kami menyulitkan Anda untuk membuat contoh kecil mandiri sehingga orang dapat bereksperimen dengan itu? Sepertinya Anda mungkin melakukan setidaknya sebagian dari yang di atas, tapi saya tidak yakin. In input f 444 root root dd if=/dev/sda1 bs=1024 count=10is f input file? Mungkin akan lebih baik untuk membuat perangkat mainan, mengisinya dengan data, dan menulis darinya? Dan apakah semua ini membutuhkan root?
Faheem Mitha
@FaheemMitha - ya saya bisa melakukan itu, tapi saya tidak melakukannya di sini. Tautan ke dokumentasi resmi - diambil langsung dari situ. Akan lebih baik jika saya melakukan contoh perintah. Saya pernah melakukannya sebelumnya - ini sangat keren. Pokoknya - inputfile tersebut adalah file dalam squashfsarsip - gambar filesystem yang dihasilkan dari menjalankan perintah. Ketika Anda melakukannya, mksquashAnda dapat menentukan perintah pseudofile ini untuk perintah yang dijalankan dan dari mana stdoutditangkap pada waktu kompres.
mikeserv
@FaheemMitha - oh, dan itu tidak memerlukan root untuk melakukan kompresi , meskipun mungkin untuk melakukan pemasangan - itu adalah gambar filesystem yang dihasilkan. Ini adalah sistem file yang sama dengan semua disk Linux Live. Bahkan - satu hal yang sangat keren - adalah bahwa Anda dapat membuat gambar yang dimiliki root menggunakan file-file semu tanpa menjadi root - seperti mengatur file perangkat Anda dan nomor MAJ: MIN yang sewenang-wenang.
mikeserv
Saya kira itu mungkin untuk membuat file perangkat, menulis ke sana, dan kemudian dari itu tanpa pernah memasangnya, kan? Jadi, mungkin itu tidak memerlukan root, yang jelas akan lebih disukai.
Faheem Mitha
Yah, tidak ada btrf yang terlibat di sini, jadi itu tidak akan berhasil. Tapi squashfs cukup gila mungkin berhasil. Meskipun memiliki kelemahan karena tidak menjadi format arsip umum.
derobert
4

Masalah Anda membingungkan saya selama beberapa waktu, dan saya pikir saya telah menemukan solusi yang akan berhasil.

Saya pikir Anda dapat mencapai apa yang Anda inginkan dengan 7z menggunakan -si{NAME}bendera.

Anda akan dapat beradaptasi dengan kebutuhan Anda.

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 : Hapus penggunaan kucing yang tidak berguna

Tony
sumber
Akan sangat membantu untuk memiliki contoh kecil yang dapat dicoba orang. Misalnya membuat perangkat blok, menulis padanya, lalu menulis darinya. Tidak membutuhkan root akan menjadi nilai tambah.
Faheem Mitha
Dalam contoh / dev / sda1 adalah perangkat blok. Perintah cat memiliki tujuan membuang konten perangkat ke stdout. Kemudian 7z membuat (atau memperbarui) arsip, dan menyimpan data dalam nama file yang ditentukan oleh parameter -si dari stdin. Hasil dalam arsip adalah konten dari setiap perangkat blok. Perintah "cat" membutuhkan root untuk membaca data dari perangkat.
Tony
Itu adalah Penggunaan Cat yang Tidak Berguna , tetapi sebaliknya cocok dengan tagihan. Anehnya 7zhalaman manual saya tidak menyebutkan -si bisa mengambil nama file, tetapi berhasil. Ini tidak sempurna (output tidak dapat disalurkan ke suatu tempat), tetapi jelas merupakan yang terbaik sejauh ini dalam format umum.
derobert
@FaheemMitha yang membutuhkan root atau tidak akan bergantung pada pengaturan izin pada sistem Anda, meskipun hanya root yang dapat membuat perangkat blok baru.
derobert
@derobert Removed the cat :)
Tony