Как убедить tar (и т. Д.) Архивировать содержимое блочного устройства?

13

У меня есть шесть логических томов Linux, которые вместе поддерживают виртуальную машину. В настоящее время виртуальная машина отключена, поэтому ее легко получать последовательные изображения.

Я хотел бы упаковать все шесть изображений в архив. Я мог бы сделать что-то вроде этого:

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

Но это, конечно, создает дополнительную копию. Я хотел бы избежать дополнительной копии.

Очевидный подход:

tar c /dev/Zia/vm_lvraid_* | whatever

не работает, так как tar распознает файлы специальным образом (в данном случае символические ссылки) и в основном сохраняет их ln -sв архиве. Или, с --dereferenceуказанием или прямым указанием /dev/dm-X, он распознает их как специальные (файлы устройств) и в основном сохраняет их mknodв архиве.

Я искал параметры командной строки для tar, чтобы переопределить это поведение, и не смог найти ни одного. Я также пытался cpio, та же проблема, и не мог найти какие-либо варианты переопределить там, либо. Я тоже пытался 7z(так же). То же самое с pax. Я даже попробовал zip, который просто запутался.

edit: Глядя на исходный код GNU tar и GNU cpio, кажется, что ни один из них не может этого сделать. По крайней мере, не без серьезных хитростей (специальная обработка файлов устройств не может быть отключена). Таким образом, предложения серьезного обмана будут оценены или альтернативные утилиты.

TLDR: существует ли какой-нибудь архиватор, который будет упаковывать несколько образов дисков (взятых с необработанных устройств) и передавать их на поток без создания дополнительных копий на диске? Мои предпочтения будут выводиться в общем формате, таком как POSIX или GNU tar.

derobert
источник
Я убедил это.
mikeserv

Ответы:

11

Так недавно я хотел сделать это с tar. Некоторое расследование показало мне, что это было немного глупо, что я не мог. Я придумал эту странную split --filter="cat >file; tar -r ..."вещь, но, ну, это было ужасно медленно. И чем больше я читаю о tarбессмысленности, тем больше это казалось.

Видите ли, tarэто просто составной список записей. Составляющие файлы никак не изменяются - они целые в архиве. Но они блокируются на границах блоков по 512 байт , и перед каждым файлом есть заголовок . Вот и все. Формат заголовка действительно очень прост.

Итак, я написал свой tar. Я называю это ... 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"
}

Это мясо и картошка, правда. Он записывает заголовки и вычисляет chksum - что, собственно говоря, является единственной сложной частью. Это делает ustarформат заголовка ... возможно . По крайней мере, он подражает тому, что GNU, tarкажется, считает ustarформат заголовка до такой степени, что он не жалуется. И это еще не все, просто я еще не коагулировал это. Здесь я покажу вам:

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

Это tar. Все заполнено \0нулями, поэтому я просто превращаюсь emв электронные \nстроки для удобства чтения. И 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 .

ВЫХОД

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

Я говорю вроде там , потому что это не shitarцель «s - tarуже делает это красиво. Я просто хотел показать, как это работает - а это значит, что мне нужно коснуться chksum. Если бы не это, я бы просто ddснял с головы tarфайл и покончил с этим. Это может даже иногда работать, но это становится грязным, когда в архиве несколько участников. Тем не менее, chksum действительно легко.

Во-первых, сделайте это 7 пробелами - (что странно для гну, я думаю, как сказано в спецификации 8, но что угодно - взлом - это взлом) . Затем сложите восьмеричные значения каждого байта в заголовке. Это твой чксум. Таким образом, вам нужны метаданные файла, прежде чем вы сделаете заголовок, или у вас нет chksum. И это в ustarосновном архив.

Ok. Теперь, что это должно сделать:

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

Это создает три 500M-образа диска, форматирует и монтирует каждый и записывает файл в каждый.

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

Примечание - очевидно, блокирующие устройства просто всегда будут правильно блокироваться. Довольно удобно.

Это tarсодержимое файлов дискового устройства, находящихся в потоке и передающее вывод 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

Теперь момент истины ...

 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

Ура! Добыча ...

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

Сравнение ...

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

И гора ...

sudo mount disk13 mnt
cat mnt/*
disk3 file3

И так, в этом случае, shitarработает нормально, я думаю. Я бы предпочел не вдаваться во все вещи, которые не принесут успеха. Но я скажу - не делайте переводы строк в именах файлов по крайней мере.

Вы также можете сделать - и, возможно, следует, учитывая альтернативы, которые я предложил, - с этим squashfs. Вы не только получаете единый архив, созданный из потока, но и mountможете встроить его в ядро vfs:

Из псевдо-файла. Пример :

# 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

Вы также можете использовать btrfs (send|receive)поток тома в любом stdin-компилируемом компрессоре, который вам нравится. Конечно, этот объем не обязательно должен существовать, прежде чем вы решите использовать его в качестве контейнера сжатия.

Тем не менее, о squashfs...

Я не верю, что делаю это справедливо. Вот очень простой пример:

 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

Это только встроенный -pаргумент для mksquash. Вы можете найти файл, -pfсодержащий столько, сколько вам нужно. Формат прост - вы определяете имя / путь целевого файла в файловой системе нового архива, вы задаете ему режим и владельца, а затем указываете, из какого процесса выполнять и считывать стандартный вывод. Вы можете создавать столько, сколько хотите - и вы можете использовать LZMA, GZIP, LZ4, XZ ... хм, есть еще ... форматы сжатия, как вам нравится. И конечным результатом является архив, в который вы cd.

Больше о формате, хотя:

Это, конечно, не просто архив - это сжатый, монтируемый образ файловой системы Linux. Его формат - ядро ​​Linux - это файловая система, поддерживаемая ядром ванили. Таким образом, это так же часто, как ванильное ядро ​​Linux. Так что, если бы вы сказали мне, что у вас запущенная система Linux, на которой tarпрограмма не была установлена, я был бы сомнителен, но я бы, вероятно, поверил вам. Но если бы вы сказали мне, что у вас запущенная система Linux, в которой squashfsфайловая система не поддерживается, я бы вам не поверил.

mikeserv
источник
Майк, не могли бы мы побеспокоить тебя, чтобы создать небольшой автономный пример, чтобы люди могли поэкспериментировать с ним? Похоже, вы, возможно, делаете хотя бы часть этого, но я не уверен. В input f 444 root root dd if=/dev/sda1 bs=1024 count=10какой файл ввода? Возможно, было бы лучше создать игрушечное устройство, заполнить его данными и написать с него? И требует ли все это рута?
Фахим Митха
@FaheemMitha - да, я могу это сделать, но я не делал это здесь. Ссылка на официальную документацию - она ​​взята прямо из нее. Было бы лучше, если бы я сделал командный пример. Я делал это раньше - это круто. В любом случае - inputфайл - это файл в squashfsархиве - образ файловой системы, полученный в результате выполнения команды. Когда вы это сделаете, mksquashвы можете указать эти команды псевдофайла для команд, которые запускаются и из которых stdoutзахватывается во время сжатия.
mikeserv
@FaheemMitha - о, и он не требует, чтобы root выполнял сжатие , хотя может и монтировать - это образ файловой системы, который получается. Это та же файловая система, которую используют все диски Linux Live. На самом деле - одна очень крутая вещь - это то, что вы можете создавать изображения, принадлежащие пользователю root, используя эти псевдофайлы, не будучи пользователем root, например, устанавливать файлы устройства и произвольные числа MAJ: MIN.
mikeserv
Я думаю, должно быть возможно создать файл устройства, записать в него, а затем из него, даже не монтируя его, верно? Так что, возможно, для этого не требуется root, что, очевидно, было бы предпочтительнее.
Фахим Митха
Ну, здесь нет btrfs, так что это не сработает. Но squashfs достаточно сумасшедший, это может сработать. Хотя есть и недостаток в том, что он не является общим форматом архива.
Дероберт
4

Ваша проблема на некоторое время озадачила меня, и я думаю, что нашел решение, которое сработало бы.

Я думаю, что вы можете достичь того, что вы хотите с 7z, используя -si{NAME}флаг.

Вы сможете адаптироваться к вашим потребностям.

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

РЕДАКТИРОВАТЬ : удалить бесполезное использование кошки

Тони
источник
Было бы полезно иметь небольшой пример, который люди могут опробовать. Например, создайте блочное устройство, напишите на него, затем запишите с него. Не требующий рут будет плюсом.
Фахим Митха
В примере / dev / sda1 является блочным устройством. Цель команды cat - выгрузить содержимое устройства в stdout. Затем 7z создает (или обновляет) архив и сохраняет данные в имени файла, указанном параметром -si из stdin. Результатом в архиве является содержимое каждого блочного устройства. Команде "cat" нужен root для чтения данных с устройства.
Тони
Это бесполезное использование кошки , но в остальном хорошо отвечает всем требованиям. Как ни странно, моя 7zсправочная страница не упоминает -si может взять имя файла, но это работает. Он не идеален (вывод не может быть куда-то передан), но определенно является лучшим, если вывод в общем формате.
Дероберт
@FaheemMitha, требующий root или нет, будет зависеть от настроек разрешений в вашей системе, хотя только root может создавать новые блочные устройства.
Дероберт
@derobert удалил кошку :)
Тони