前言
笔者曾经在Linux下的备份方式小结一文中总结过Linux下的备份方式,其中就提到过使用Squashfs进行数据存档与备份的方法。
事实上,Squashfs的适用场景不仅仅局限于备份,它还可以用于日常归档压缩,甚至可以用于替代传统的压缩格式。Squashfs的支持也非常广泛,几乎所有的Linux发行版都支持Squashfs,而Windows和macOS也都有Squashfs的解压工具;对于zstd
等相对较新的算法,在其他平台下打开采用对应压缩方式的Squashfs则可能需要安装额外的软件,例如7-Zip-zstd。
Squashfs的优势
Squashfs是一种只读文件系统,相较于传统的zip
、tar.gz
、tar.xz
、tar.zst
等压缩格式,Squashfs的主要优势是它是一个完整的文件系统,在Linux下可以直接挂载使用,而不需要解压缩。同时,Squashfs在使用合适的压缩算法及分块大小时,能够兼有高压缩率与高访问速度。另外,Squashfs还支持文件级别重复数据删除,进一步节省空间。
基础用法
在不追求极致压缩率的场景下,笔者推荐使用zstd:3
的压缩算法,这样可以节省空间,而且压缩速度非常快、CPU占用很小。根据zstd
算法的特性,使用较高压缩等级的zstd
算法可以在牺牲一定创建压缩的速度的情况下获得更高的压缩率,但是并不会明显降低透明的解压缩的速度,因此,如果需要更高的压缩率,可以考虑使用更高等级的zstd
压缩算法,例如将压缩等级设置为6 - 15。
笔者个人不太推荐使用解压性能相对较差的gzip
、xz
等算法,因为这些算法的压缩率提升相较于zstd
有限,而且解压速度较慢,不适合创建后的快速访问。
如果需要比zstd
更快的透明访问/解压速度,可以考虑使用lz4
算法,lz4
的解压速度非常快,但是在实际场景中,zstd
的速度其实也已经足够快了,而且在压缩率上一般还有一定的优势。(PS. zstd
和lz4
的作者其实是同一个人)
需要注意的是,mksquashfs
的基本命令格式为:
mksquashfs source1 source2 ... FILESYSTEM [OPTIONS]
[OPTIONS]
必须放到FILESYSTEM
之后,否则会报错。
文件/目录级归档
如果需要归档已经挂载的文件系统上的文件,可以直接使用mksquashfs
,例如:
# 归档 /home 目录
sudo mksquashfs /home /XXX/目标目录/backup.sfs -comp zstd -Xcompression-level 3
# 归档 / 目录
sudo mksquashfs / /XXX/目标目录/backup.sfs -comp zstd -Xcompression-level 3 -e /dev -e /proc -e /sys -e /tmp -e /run -e /mnt -e /media -e /lost+found -e /boot/efi -e /efi
# 归档 / 子卷
sudo mksquashfs / /XXX/目标目录/backup.sfs -comp zstd -Xcompression-level 3 --one-file-system
这里只是用于演示较彻底的归档备份,实际使用中如果只用归档部分需要归档存储的内容,往往并不需要sudo
权限,例如:
# 归档 abc 目录
mksquashfs abc /XXX/目标目录/backup.sfs -comp zstd -Xcompression-level 3
像这样创建的squashfs是一个直接包含归档文件的文件系统,可以直接挂载使用。
块设备级备份
有时候我们可能需要备份某些在Linux下不方便直接挂载并备份的分区,比如受BitLocker加密的NTFS分区等,这时,为了完成保留整个硬盘设备的信息,我们可以直接备份整个硬盘设备。如果需要备份整个硬盘设备,可以使用dd
和mksquashfs
结合,即,用mksquashfs
调用dd
的输入流,例如:
sudo mksquashfs - /XXX/目标目录/backup.sfs -p "backup.img f 0644 root root dd if=/dev/nvme0n1 bs=1M" -p "/ d 0755 0 0" -comp zstd -Xcompression-level 3
这里的-
表示标准输入流,-p
表示添加文件,-p
后面的内容是mksquashfs
的文件描述符,f
表示类型为文件,后面紧跟的是权限信息,dd if=/dev/nvme0n1 bs=1M
表示调用dd
的输入流,if=/dev/nvme0n1
表示输入文件,bs=1M
表示块大小;/
表示根目录,d
表示类型为目录,后面同样跟有权限信息。
整体上,这个命令的意思是,将/dev/nvme0n1
的内容作为输入流,作为backup.img
文件的内容,将backup.img
文件放在创建的squashfs
文件系统的根目录,然后压缩成squashfs
文件系统。这样既保留了整个硬盘的完整存储信息,又启用压缩,节省了空间。不过,从这样的备份中读取内容时,需要先挂载squashfs
文件系统,然后加载其中的backup.img
文件为loop设备,再挂载所需要挂载的分区。
将其他归档压缩格式转换为Squashfs
有时候,我们现有的归档压缩文件可能并不是Squashfs,而是其他格式,例如tar.zst
、tar.gz
、tar.xz
、zip
等,这时,我们为了得到既节约空间,又不用解压缩就可以直接挂载使用的归档文件,往往需要将这些归档文件转换为Squashfs。
最容易想到的方法是,先解压缩归档文件,然后再使用mksquashfs
创建Squashfs。然而,这样做会解压产生很多中间文件,占用大量的空间,而且需要花费大量的IO时间。
笔者在这里推荐充分利用Linux的管道特性,直接将归档文件的解压缩输出作为mksquashfs
的输入流。这样,我们可以在不产生中间文件的情况下,将归档文件转换为Squashfs。
转化tar.xxx
到Squashfs
mksquashfs
工具支持从标准输入流读取数据,可以用-
代表标准输入流stdin
;同时,-tar
参数可以指定从标准输入流读取tar格式的数据,因此,我们可以使用相应压缩软件gz
、zstd
等命令的输出作为mksquashfs
的输入流。
笔者在这里示出一个例子,小米发布的线刷包是一个tar.gz
格式的归档文件,在使用时,一般需要先解压,然而,这势必会浪费大量的时间。而如果直接在本地存储解压后的文件,又会浪费大量的空间。因此,我们可以使用以下命令,将tar.gz
格式的线刷包转换为Squashfs,既保证了压缩率,又不需要额外的解压缩过程:
gzip -cd zircon_images_V14.0.8.0.TNOCNXM_20231129.0000.00_13.0_cn_f63a143fa2.tgz | mksquashfs - zircon_images_V14.0.8.0.TNOCNXM_20231129.0000.00_13.0_cn_f63a143fa2.sfs -tar -p "/ d 0755 0 0" -comp zstd -Xcompression-level 6
这里的gzip -cd
表示解压缩(-c
参数)tar.gz
文件并输出到标准输出流stdout
(-d
参数),|
表示管道,mksquashfs -
表示将标准输入流作为mksquashfs
的输入流,-tar
表示从标准输入流读取未压缩的tar数据,-p
表示定义Squashfs中的文件组织,/ d 0755 0 0
表示指定Squashfs文件系统的根目录,d
表示类型为目录,后面跟有权限信息,-comp zstd -Xcompression-level 6
表示使用zstd
压缩,压缩等级为6
。另外需要注意的是,解压命令需要与原压缩包的压缩格式相匹配,例如,如果原压缩包是tar.xz
格式,那么解压命令应该是xz -cd
,如果原压缩包是tar.zst
格式,那么解压命令应该是zstd -cd
。
这样,我们就在不产生中间文件的情况下,将tar.xxx
格式的线刷包转换为了Squashfs,可以直接挂载使用。
当然,对于本身即未压缩的tar数据,我们也可以直接使用mksquashfs
的-tar
参数,直接用cat
传入即可,例如:
cat example.tar |mksquashfs - example.sfs -tar -comp zstd -Xcompression-level 6
此外,如果本地还没有下载tar.xxx
格式的归档文件,我们也可以直接使用wget
的输出流进行转化,例如:
wget -O - https://example.com/example.tar.gz | gzip -d | mksquashfs - example.sfs -tar -comp zstd -Xcompression-level 6
这样,我们就在不产生中间文件的情况下,将远程的tar.xxx
格式的归档文件转换为了Squashfs,可以直接挂载使用。
转化zip
到Squashfs
由于mksquashfs
工具可以高效地从标准输入流stdin
中读取未压缩的tar数据,我们可以用类似的方法处理zip
格式的归档文件,但细节上有所不同。
一般的zip
归档文件通常与tar.xxx
不同,它的归档与压缩均由zip
完成,内部并不含有未压缩的tar数据。因此,我们在这里不能直接使用unzip
的输出流进行管道操作。
幸运的是,bsdtar
这一强大的工具可以直接读取zip
格式的归档文件(只需要在文件路径前面加上@
),并将其转化为tar流,通过管道正常传输给mksquashfs
。因此,我们可以使用以下命令,将zip
格式的归档文件转换为Squashfs:
bsdtar -c @test.zip | mksquashfs - test.sfs -tar -p "/ d 0755 0 0" -comp zstd -Xcompression-level 6
这里的bsdtar -c @test.zip
表示将test.zip
文件转换为tar流并输出到标准输出流stdout
,|
表示管道,mksquashfs -
表示将标准输入流作为mksquashfs
的输入流,-tar
表示从标准输入流读取未压缩的tar数据,-p
表示定义Squashfs中的文件组织,/ d 0755 0 0
表示添加根目录,d
表示类型为目录,后面跟有权限信息,-comp zstd -Xcompression-level 6
表示使用zstd
压缩,压缩等级为6
。
这样,我们就在不产生中间文件的情况下,将zip
格式的归档文件转换为了Squashfs,可以直接挂载使用。
bsdtar
直接读取归档的特性不仅仅适用于zip
格式的归档文件,还适用于tar
, pax
, cpio
, jar
, ar
, xar
, rpm
, 7z
, ISO 9660 CDROM
,以上格式均可利用类似方法转换为Squashfs。
Squashfs到Squashfs的转化
有时候,我们可能需要更换Squashfs的压缩算法、块大小、压缩等级等参数,这时,我们必须要另外创建新的Squashfs。虽然Squashfs可以直接挂载使用,即使先挂载,再使用mksquashfs
也没有任何解压的中间文件产生;但是,这样的操作其实是在随机访问已挂载的Squashfs,IO性能可能较差。如果需要一次性访问Squashfs中的所有文件,先挂载再访问的方式一般会比使用unsquashfs
慢。
然而,如果直接使用unsquashfs
解压缩Squashfs,又会产生大量的中间文件,占用大量的空间,而且需要花费大量的IO时间。因此,我们可以使用类似的方法,将unsquashfs
的输出作为mksquashfs
的输入流,这样,我们就可以在不产生中间文件的情况下,将Squashfs转换为新的Squashfs。例如:
unsquashfs -pf - source.sfs | mksquashfs - output.sfs -pf - -comp zstd -Xcompression-level 6 -b 16K
这里的unsquashfs -pf - source.sfs
表示将source.sfs
文件解压缩到标准输出流stdout
,同时也一并输出了文件组织的结构、权限等信息,|
表示管道,mksquashfs -
表示将标准输入流作为mksquashfs
的输入流,-pf -
表示从标准输入流读取文件组织的结构、权限等信息,-comp zstd -Xcompression-level 6
表示使用zstd
压缩,压缩等级为6
,-b 16K
表示块大小为16K
。
这样,我们就在不产生中间文件的情况下,利用unsquashfs
更优秀的IO性能,将原来的Squashfs快速转换为了新的Squashfs,并重新指定了压缩算法、块大小、压缩等级等参数。