我有六个 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
,但这只是让自己感到困惑。
编辑:查看 GNU tar 和 GNU cpio 的源代码,似乎它们都不能做到这一点。至少,并非没有严重的欺骗(无法禁用设备文件的特殊处理)。因此,严重欺骗的建议将受到赞赏或替代实用程序。
总括:是否有一些归档程序可以将多个磁盘映像打包在一起(从原始设备获取)并流式传输输出,而无需制作额外的磁盘副本?我更喜欢以通用格式输出,例如 POSIX 或 GNU tar。
答案1
所以最近我想用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"
}
确实,这就是肉和土豆。它写入标头并计算校验和 - 相对而言,这是唯一困难的部分。它执行ustar
标头格式...或许。至少,它模拟了 GNUtar
认为的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
为ewlines。\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
的目的 -tar
已经做得很漂亮了。我只是想展示它是如何工作的 - 这意味着我需要触及chksum
.如果不是因为这个,我就会直接dd
删除文件的头部tar
并完成它。有时这甚至可能有效,但当档案中有多个成员时,它会变得混乱。不过,chksum 确实很简单。
首先,将其设为 7 个空格 -(我认为这是一个奇怪的 gnu 事情,正如规范所说的 8,但无论如何 - 黑客就是黑客)。然后将标头中每个字节的八进制值相加。那是你的校验和。因此,在处理标头之前,您需要文件元数据,否则您就没有校验和。这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
笔记- 显然,阻塞设备总是会正确阻塞。非常方便。
这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
我不会相信你。
答案2
你的问题困扰了我一段时间,我想我已经找到了可行的解决方案。
我认为你可以使用 7z 标志来实现你想要的-si{NAME}
。
您将能够根据您的需要进行调整。
7z a test.7z -siSDA1.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
编辑:删除cat的无用使用
答案3
如果您不关心 tar 是否执行此操作,并且您想要的只是块设备内容的存档,则可以使用 ddrescue 它将设备的二进制映像生成为您选择的文件名。安装程序(对于 Debian)称为 gddrescue。
创建映像后,您可以使用任何映像编写器来恢复它,甚至可以安装映像以查看(和复制)其中的内容。