Як переконати tar (тощо) в архівуванні вмісту блокового пристрою?


13

У мене є шість логічних томів Linux, які разом підтримують віртуальну машину. В даний час VM вимикається, тому їх легко робити послідовними зображеннями.

Я хотів би запакувати всі шість зображень разом в архів. Тривіально, я міг би зробити щось подібне:

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в архіві.

Я шукав параметри командного рядка, щоб визначити, як перекрити цю поведінку, і не знайшов жодної. Я також спробував cpio, з тією ж проблемою, і не зміг знайти жодних варіантів, щоб перекрити її там. Я також спробував 7z(дітто). Те саме з pax. Я навіть спробував zip, що просто заплутався.

редагувати: дивлячись на вихідний код смоли GNU та GNU cpio, схоже, жоден із них не може цього зробити. Принаймні, не без серйозних хитрощів (спеціальну обробку файлів пристрою неможливо відключити). Отже, пропозиції серйозних хитрощів будуть вдячні або альтернативні програми.

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


Я переконав це.
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на \newlines для читабельності. І 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файлова система не підтримується, я б вам не повірив.


Майку, ми могли б завадити тобі створити невеликий автономний приклад, щоб люди могли експериментувати з ним? Здається, ви можете зробити принаймні частину цього, але я не впевнений. Чи input f 444 root root dd if=/dev/sda1 bs=1024 count=10є введення файлу? Можливо, було б краще створити іграшковий пристрій, наповнити його даними та написати з нього? І чи все це вимагає кореня?
Faheem Mitha

@FaheemMitha - так, я можу це зробити, але я цього не робив. Посилання на офіційну документацію - воно взято прямо з неї. Було б краще, якби я зробив приклад команди. Я робив це раніше - досить класно. У будь-якому випадку - inputфайл - це файл в squashfsархіві - зображення файлової системи, що є результатом виконання команди. Після цього mksquashви можете вказати ці псевдофайлові команди для команд, які виконуються і з яких stdoutбуде записано під час стиснення.
mikeserv

@FaheemMitha - о, і він не вимагає кореня робити стиснення , хоча це може зробити монтаж - це результат файлової системи. Це та сама файлова система, яку використовують усі диски Linux Live. Насправді - одна дуже прикольна річ - це те, що ви можете створювати зображення, що належать до кореня, використовуючи ці псевдофайли, не будучи кореневими - як встановлення файлів вашого пристрою та довільних чисел MAJ: MIN.
mikeserv

Напевно, має бути можливість створити файл пристрою, записати в нього, а потім із нього, навіть не монтуючи його, правда? Тож, можливо, для цього не потрібен корінь, що, очевидно, було б кращим.
Faheem Mitha

Ну, тут немає жодних btrfs, тому це не спрацює. Але сквош досить божевільний, що може спрацювати. Хоча це є і недоліком того, що він не є загальним форматом архіву.
дероберт

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

РЕДАКТУВАТИ : Видаліть марне використання кота


Було б корисно мати невеликий приклад, який люди можуть спробувати. Наприклад, створіть блок-пристрій, запишіть його, а потім випишіть з нього. Якщо не вимагати кореня, це буде плюсом.
Faheem Mitha

У прикладі / dev / sda1 - блок пристроїв. Команда cat має на меті скинути вміст пристрою в stdout. Потім 7z створити (або оновити) архів і зберегти дані у назві файлу, визначеному параметром -si від stdin. Результатом в архіві є вміст кожного блоку (пристроїв). Команда "cat" потребує кореня, щоб прочитати дані з пристрою.
Тоні

Це марне використання кота , але в іншому випадку досить добре підходить до рахунку. Як не дивно моя сторінка 7zне згадує -si може взяти ім’я файлу, але це працює. Це не ідеально (вихід не може бути кудись записаний), але, безумовно, найкращий поки що, що виводиться у загальному форматі.
дероберт

@FaheemMitha, що вимагає root або не, буде залежати від налаштувань дозволу у вашій системі, хоча тільки root може створювати нові блокові пристрої.
дероберт

@derobert Вилучив кота :)
Тоні
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.