Так недавно я хотел сделать это с 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
файловая система не поддерживается, я бы вам не поверил.