วิธีการโน้มน้าว tar (ฯลฯ ) เพื่อเก็บเนื้อหาของอุปกรณ์บล็อกได้อย่างไร


13

ฉันมีลอจิคัลวอลุ่มหกที่รวมกันเป็นเครื่องเสมือน VM กำลังปิดตัวลงดังนั้นจึงง่ายต่อการถ่ายภาพที่สอดคล้องกัน

ฉันต้องการจัดเก็บทั้งหกภาพไว้ในที่เก็บถาวร ฉันสามารถทำสิ่งนี้ได้:

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

แต่แน่นอนว่าสร้างสำเนาเพิ่มเติม ฉันต้องการหลีกเลี่ยงการคัดลอกพิเศษ

วิธีการที่ชัดเจน:

tar c /dev/Zia/vm_lvraid_* | whatever

ไม่ทำงานเนื่องจาก tar รู้จักไฟล์เป็นพิเศษ (symlink ในกรณีนี้) และโดยทั่วไปจะจัดเก็บln -sในไฟล์เก็บถาวร หรือด้วย--dereferenceหรือชี้ไปที่/dev/dm-Xมันรู้จักเป็นพิเศษ (ไฟล์อุปกรณ์) และโดยทั่วไปเก็บไว้mknodในที่เก็บ

ฉันค้นหาตัวเลือกบรรทัดคำสั่งเพื่อ tar เพื่อแทนที่พฤติกรรมนี้และไม่พบสิ่งใด ฉันพยายามcpioด้วยปัญหาเดียวกันและไม่พบตัวเลือกใด ๆ ที่จะแทนที่ที่นั่นเช่นกัน ฉันก็ลอง7z(เหมือนกัน) paxเช่นเดียวกันกับ ฉันพยายามzipแล้วซึ่งทำให้สับสน

แก้ไข: ดูซอร์สโค้ดของ GNU tar และ 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ที่ ทุกอย่างเต็มไปด้วย\0nulls ดังนั้นฉันจึงเปลี่ยน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จุดประสงค์ - tarทำอย่างนั้นเรียบร้อยแล้ว ฉันแค่อยากจะแสดงวิธีการทำงาน - chksumซึ่งหมายความว่าฉันจะต้องสัมผัสกับ ถ้าไม่ใช่เพราะสิ่งนั้นฉันก็แค่ddถอดหัวtarไฟล์ออกแล้วทำมัน แม้บางครั้งอาจใช้งานได้ แต่มันก็ยุ่งเหยิงเมื่อมีสมาชิกหลายคนในที่เก็บถาวร ถึงกระนั้น chksum ก็ง่ายจริงๆ

ก่อนให้มัน 7 ช่องว่าง - (ซึ่งเป็นสิ่งที่แปลก GNU ผมคิดว่าเป็นสเปคกล่าวว่า 8 แต่สิ่งที่ - สับเป็นสับ) จากนั้นเพิ่มค่าฐานแปดของทุกไบต์ในส่วนหัว นั่นคือ chksum ของคุณ ดังนั้นคุณต้องมีเมทาดาทาไฟล์ก่อนที่จะทำส่วนหัวหรือคุณไม่มี 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

นั่นทำให้ดิสก์อิมเมจขนาด 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

หมายเหตุ - อุปกรณ์บล็อกที่เห็นได้ชัดจะปิดกั้นอย่างถูกต้องเสมอ ค่อนข้างมีประโยชน์

นั่นคือเนื้อหาของแฟ้มอุปกรณ์ดิสก์ในสตรีมและท่อออกไปtarxz

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โอเคฉันเดา ฉันไม่อยากทำทุกสิ่งที่ไม่ดี แต่ฉันจะบอกว่า - อย่าทำบรรทัดใหม่ในชื่อไฟล์อย่างน้อยที่สุด

นอกจากนี้คุณยังสามารถทำ - และอาจควรพิจารณาทางเลือกที่ผมเคยนำเสนอ -this 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 ... hmm มีรูปแบบการบีบอัดมากกว่าที่คุณต้องการ cdและผลลัพธ์ที่ได้คือที่เก็บที่คุณ

เพิ่มเติมเกี่ยวกับรูปแบบที่:

แน่นอนว่านี่ไม่ใช่แค่ไฟล์เก็บถาวรเท่านั้น แต่เป็นอิมเมจระบบไฟล์บีบอัดที่ mountable ได้ รูปแบบของมันคือลีนุกซ์เคอร์เนล - เป็นระบบไฟล์เคอร์เนลที่รองรับวานิลลา ด้วยวิธีนี้มันเป็นเรื่องธรรมดาเหมือนเคอร์เนลวานิลลา ดังนั้นถ้าคุณบอกฉันว่าคุณกำลังใช้ระบบวานิลลาลินุกซ์ซึ่งtarไม่ได้ติดตั้งโปรแกรมฉันจะสงสัย - แต่ฉันอาจจะเชื่อคุณ แต่ถ้าคุณบอกฉันว่าคุณกำลังใช้ระบบวานิลลาลินุกซ์ซึ่งsquashfsระบบไฟล์ไม่รองรับฉันจะไม่เชื่อคุณ


ไมค์เราจะสร้างปัญหาให้คุณสร้างตัวอย่างเล็ก ๆ ที่มีในตัวเองเพื่อให้ผู้คนสามารถทดลองกับมันได้หรือไม่? ดูเหมือนว่าคุณอาจทำอย่างน้อยส่วนหนึ่งของข้างต้น แต่ฉันไม่แน่ใจ ในinput f 444 root root dd if=/dev/sda1 bs=1024 count=10การป้อนไฟล์ f คืออะไร? บางทีมันอาจจะเป็นการดีกว่าถ้าคุณสร้างอุปกรณ์ของเล่นกรอกข้อมูลและเขียนจากมัน? ทั้งหมดนี้ต้องใช้รากหรือไม่?
Faheem Mitha

@FaheemMitha - ใช่ฉันทำได้ แต่ฉันไม่ได้ทำที่นี่ ลิงค์นี้เป็นเอกสารทางการ - นำมาจากลิงก์โดยตรง มันจะดีกว่าไหมถ้าฉันทำตัวอย่างคำสั่ง ฉันเคยทำมาก่อน - มันค่อนข้างเจ๋ง อย่างไรก็ตาม - inputไฟล์เป็นไฟล์ในไฟล์squashfsเก็บถาวร - อิมเมจระบบไฟล์ที่เป็นผลมาจากการรันคำสั่ง เมื่อคุณทำmksquashคุณสามารถระบุคำสั่ง pseudofile เหล่านี้สำหรับคำสั่งที่รันและที่stdoutถูกดักจับในเวลาบีบอัด
mikeserv

@FaheemMitha - โอ้และไม่จำเป็นต้องรูทเพื่อทำการบีบอัดแม้ว่ามันอาจจะทำการติดตั้ง - มันเป็นอิมเมจระบบไฟล์ที่ให้ผลลัพธ์ มันเป็นระบบไฟล์เดียวกันกับดิสก์ Linux Live ทั้งหมดที่ใช้ อันที่จริงสิ่งหนึ่งที่เจ๋งมากคือคุณสามารถสร้างอิมเมจที่เป็นเจ้าของรูทโดยใช้ไฟล์หลอกเหล่านั้นโดยไม่ต้องรูทเช่นการตั้งค่าไฟล์อุปกรณ์และหมายเลข MAJ: MIN โดยพลการ
mikeserv

ฉันเดาว่ามันควรจะเป็นไปได้ที่จะสร้างไฟล์อุปกรณ์เขียนลงไปและจากนั้นโดยไม่ต้องทำการติดตั้งใช่ไหม? ดังนั้นอาจไม่ต้องการรูทซึ่งเห็นได้ชัดว่าน่าจะดีกว่า
Faheem Mitha

ดีไม่มี btrfs เกี่ยวข้องที่นี่เพื่อที่จะไม่ทำงาน แต่สควอชนั้นบ้าพอที่มันจะได้ผล แม้ว่ามันจะมีข้อเสียที่ไม่ใช่รูปแบบไฟล์เก็บถาวรทั่วไป
Derobert

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" ต้องการรูทเพื่ออ่านข้อมูลจากอุปกรณ์
โทนี่

นั่นคือการใช้งานที่ไร้ประโยชน์ของแมวแต่อย่างอื่นเหมาะกับบิลสวยดี หน้า7zmanpage ของฉันไม่ได้พูดถึง -si สามารถใช้ชื่อไฟล์ได้ แต่ใช้งานได้ มันไม่สมบูรณ์แบบ (ไม่สามารถส่งเอาต์พุตไปที่ใดที่หนึ่งได้) แต่ก็ดีที่สุดเท่าที่จะทำได้ในรูปแบบทั่วไป
Derobert

@FaheemMitha ต้องการรูทหรือไม่ขึ้นอยู่กับการตั้งค่าการอนุญาตบนระบบของคุณแม้ว่าเฉพาะรูทเท่านั้นที่สามารถสร้างอุปกรณ์บล็อกใหม่ได้
Derobert

@derobert ลบแมว :)
Tony
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.