Тому нещодавно я хотів це зробити 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
ewlines для читабельності. І 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 пробілами - (це дивна річ Gnu, я думаю, як пише спец. 8, але що б там не було - хак) . Потім складіть восьмі значення кожного байта в заголовку. Це твій чксум. Отже, вам потрібні метадані файлів, перш ніж робити заголовок, або у вас немає chksum. І це ustar
здебільшого архів.
Добре. Тепер, що це означає робити:
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
Це створює три образи диска, формати та монтування на 500М дисках кожного, і записує файл до кожного.
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
:
З 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
Ви також можете використати 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
що містить стільки, скільки вам подобається. Формат простий - ви визначаєте ім’я / шлях цільового файлу у файловій системі нового архіву, надаєте йому режим і власника, а потім повідомляєте йому, з якого процесу виконувати та читати stdout. Ви можете створити скільки завгодно - і ви можете використовувати LZMA, GZIP, LZ4, XZ ... хм, є більше ... форматів стиснення, скільки вам подобається. І кінцевий результат - це архів, в який ви cd
.
Більше про формат, хоча:
Це, звичайно, не просто архів - це стислий, розміщений образ файлової системи Linux. Його формат - це ядро Linux - це файлова система, що підтримується ванільним ядром. Таким чином він є настільки ж поширеним, як ядро ванілі Linux. Тож якби ви сказали мені, що ви працюєте з системою Linux ванілі, на якій tar
програма не була встановлена, я був би сумнівним - але я, мабуть, вам повірю. Але якби ви сказали мені, що ви працюєте з системою Linux ванілі, на якій squashfs
файлова система не підтримується, я б вам не повірив.