xz压缩文件的解压缩过程

[摘要]

[正文] 读文件回顾

[正文] xz解压缩过程


注意:请使用谷歌浏览器阅读(IE浏览器排版混乱)


【摘要】

本文可以作为另一篇博文: squashfs文件的读取过程 的后续。根据squashfs文件的读取过程 一文介绍,当从flash上获取了文件数据后,如果数据是压缩的还要经过解压缩,本文就为您介绍一下xz压缩文件的解压缩过程。

【正文】读文件回顾

1 首先回顾一下 squashfs文件的读取过程一文中介绍的读文件数据过程:

int squashfs_read_data(struct super_block *sb, u64 index, int length,u64 *next_index, struct squashfs_page_actor *output)
{
/* 上面squashfs_fill_super中赋值 */
struct squashfs_sb_info *msblk = sb->s_fs_info;
struct buffer_head **bh;
/* index是要操作的flash 分区内偏移地址,devlbksize=1024byte*/
int offset = index & ((1 << msblk->devblksize_log2) - 1);
/* 
flash分区内偏移地址msblk->devblksize_log2=10;cur_index表示flash分区内偏移地址对应的逻辑块;
*/
u64 cur_index = index >> msblk->devblksize_log2;
int bytes, compressed, b = 0, k = 0, avail, i;

bh = kcalloc(((output->length + msblk->devblksize - 1)
     >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
if (bh == NULL)
return -ENOMEM;

if (length) {
/*
* Datablock.读取数据块内容.
*/
bytes = -offset;
compressed = SQUASHFS_COMPRESSED_BLOCK(length);
length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
if (next_index)
*next_index = index + length;
/*output->length=512k mksquashfs时指定的块大小*/
TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
index, compressed ? "" : "un", length, output->length);

if (length < 0 || length > output->length ||
(index + length) > msblk->bytes_used)
goto read_failure;
/* 
  循环读取逻辑块;每次读取devblksize=1024大小;最先读取的逻辑块是flash分区内偏移地址所在的逻辑块. 
*/
for (b = 0; bytes < length; b++, cur_index++) {
/*获取buffer_head*/
bh[b] = sb_getblk(sb, cur_index);
if (bh[b] == NULL)
goto block_release;
/*devblksize=1024*/
bytes += msblk->devblksize;
}
/* 
1 提交一个读请求 ll_rw_block->submit_bh->submit_bio->do_blktrans_request();
2 处理读请求,真正实现驱动中的读操作 :
mtd_blktrans_work->do_blktrans_request->mtdblock_tr->mtdblock_readsect->do_cached_read->(mtd_read->mtd->_read=part_read)
->nand_read()->nand_do_read_ops()->(chip->cmdfunc)
*/
ll_rw_block(READ, b, bh);
} else {
/*
* Metadata block.读取逻辑块内容;
*/
if ((index + 2) > msblk->bytes_used)
goto read_failure;

bh[0] = get_block_length(sb, &cur_index, &offset, &length);
if (bh[0] == NULL)
goto read_failure;
b = 1;

bytes = msblk->devblksize - offset;
compressed = SQUASHFS_COMPRESSED(length);
length = SQUASHFS_COMPRESSED_SIZE(length);
if (next_index)
*next_index = index + length + 2;

TRACE("Block @ 0x%llx, %scompressed size %d\n", index,compressed ? "" : "un", length);

if (length < 0 || length > output->length ||
(index + length) > msblk->bytes_used)
goto block_release;

for (; bytes < length; b++) {
bh[b] = sb_getblk(sb, ++cur_index);
if (bh[b] == NULL)
goto block_release;
bytes += msblk->devblksize;
}
ll_rw_block(READ, b - 1, bh + 1);
}

for (i = 0; i < b; i++) {
wait_on_buffer(bh[i]);
if (!buffer_uptodate(bh[i]))
goto block_release;
}

if (compressed) {
/* 
解压缩操作,将flash上读出的数据解压缩 
msblk:超级块信息squashfs_sb_info ;
bh:buffer_head,bh->data中保存从flash上读取的数据;
b:表示读取的数据长度对应的逻辑块个数;
offset:表示读取的flash地址对应的逻辑块偏移地址,一个逻辑块为1024byte,offset=index&0x3ff
length:表示从flash上读取的数据长度;
output->length=512k mksquashfs时指定的块大小
*/
length = squashfs_decompress(msblk, bh, b, offset, length,output);
if (length < 0)
goto read_failure;
} else {
/*
* Block is uncompressed.
*/
int in, pg_offset = 0;
void *data = squashfs_first_page(output);

for (bytes = length; k < b; k++) {
in = min(bytes, msblk->devblksize - offset);
bytes -= in;
while (in) {
if (pg_offset == PAGE_CACHE_SIZE) {
data = squashfs_next_page(output);
pg_offset = 0;
}
avail = min_t(int, in, PAGE_CACHE_SIZE -
pg_offset);
memcpy(data + pg_offset, bh[k]->b_data + offset,
avail);
in -= avail;
pg_offset += avail;
offset += avail;
}
offset = 0;
put_bh(bh[k]);
}
squashfs_finish_page(output);
}

kfree(bh);
return length;

block_release:
for (; k < b; k++)
put_bh(bh[k]);

read_failure:
ERROR("squashfs_read_data failed to read block 0x%llx\n",
(unsigned long long) index);
kfree(bh);
return -EIO;
}
其中squashfs_read_data->squashfs_decompres是解压缩过程,因为此时的squashfs文件系统使用xz方式压缩,所以解压缩时使用squashfs_xz_uncompress

【正文】xz解压缩过程

下面介绍下xz方式解压缩squashfs文件:squashfs_decompress->squashfs_xz_uncompress

/*  
解压缩操作,将flash上读出的数据解压缩  
msblk:超级块信息squashfs_sb_info ; 
bh:buffer_head,bh->b_data中保存从flash上读取的数据; 
b:表示读取的数据长度对应的逻辑块个数; 
offset:表示读取的flash地址对应的逻辑块偏移地址,一个逻辑块为1024byte,offset=index&0x3ff 
length:表示从flash上读取的数据长度 
*/  
static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
	struct buffer_head **bh, int b, int offset, int length,
	struct squashfs_page_actor *output)
{
	enum xz_ret xz_err;
	int avail, total = 0, k = 0;
	struct squashfs_xz *stream = strm;
/*
xz_dec->sequence=SEQ_STREAM_HEADER;解压类型
xz_dec->temp.pos=0;解压位置;
xz_dec->temp.size=STREAM_HEADER_SIZE;需要解压12byte的头;
*/
	xz_dec_reset(stream->state);
	stream->buf.in_pos = 0;
	stream->buf.in_size = 0;
	stream->buf.out_pos = 0;
	stream->buf.out_size = PAGE_CACHE_SIZE;
	stream->buf.out = squashfs_first_page(output);
/* 
while循环中逐个逻辑块进行解压,msblk->devblksize=1024byte ;注意不一定一个循环就解压一个块,
如果一个循环内因为 xz_dec_run异常未解压完一个块内指定长度的数据,则循环解压这个块的数据,直到完成.
*/
	do {
/* 分两种情况:读取的flash地址是逻辑块大小对齐的情况和非对齐情况 ; 对齐时:offset=0;   非对齐时:offset != 0;
第一次循环时:stream->buf.in_pos==stream->buf.in_size=0;
因为之后stream->buf.in_pos会在 xz_dec_run中更新,该值表示解压进度,即已经解压到了一个块内的哪个位置(这个块中需要解压的数据是否解压完,
读取地址可能非块大小对齐,所以需要解压的数据不一定是1024即一个块的大小,如果in_pos==in_size表示这个块内需要解压的数据解压完了);
stream->buf.in_size在此初始化为一个逻辑块内真正要解压的数据大小.用xz_buf->in_size和xz_dec_run处理后的xz_buf->in_pos来判断解压进度.
*/
		if (stream->buf.in_pos == stream->buf.in_size && k < b) {
/*
此次计算一个块里需要解压的数据的大小(可能不需要解压一整个块),因为读取的地址范围可能非逻辑块对齐;
举例:假设读取flash分区内偏移位1020的地址,读取大小为10byte:则
要先读取第0个块的:1020-1024地址范围和第1个块的0-6地址范围;
*/
			avail = min(length, msblk->devblksize - offset);
			length -= avail;
/* 要解压的数据保存的地址*/
			stream->buf.in = bh[k]->b_data + offset;
/* 一个块里要解压的数据大小 */
			stream->buf.in_size = avail;
/* 一个块解压之前,解压进度当然是0 */
			stream->buf.in_pos = 0;
			offset = 0;
		}

		if (stream->buf.out_pos == stream->buf.out_size) {
			stream->buf.out = squashfs_next_page(output);
			if (stream->buf.out != NULL) {
				stream->buf.out_pos = 0;
				total += PAGE_CACHE_SIZE;
			}
		}
/*1 stream->buf=xz_buf:该结构里保存了读取到的一个逻辑块里的内容(xz_buf->in)和读取的大小(xz_buf->in_size),
因为读取长度可能非逻辑块对齐,所以读取大小也可能不是1024字节.
stream->buf.in_pos表示解压到了一个块的地址区间上的哪个位置,当一个块第一次解压时in_pos初始化为0,
只有当xz_dec_run里面全部解压完,即in_pos==in_size,时才会解压下一个逻辑块;如果xz_dec_run处理之后,
in_pos!=in_size表示该逻辑块未解压完,while循环处理中不会处理下一个块
2 stream->state=xz_dec第一次循环时使用前面xz_dec_reset()初始化的信息,表示SEQ_STREAM_HEADER;
*/
		xz_err = xz_dec_run(stream->state, &stream->buf);

		if (stream->buf.in_pos == stream->buf.in_size && k < b)
			put_bh(bh[k++]);
	} while (xz_err == XZ_OK);

	squashfs_finish_page(output);

	if (xz_err != XZ_STREAM_END || k < b)
		goto out;

	return total + stream->buf.out_pos;
out:
	for (; k < b; k++)
		put_bh(bh[k]);

	return -EIO;
}
squashfs_xz_uncompressxz_dec_run
XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b)
{
	size_t in_start;
	size_t out_start;
	enum xz_ret ret;

	if (DEC_IS_SINGLE(s->mode))
		xz_dec_reset(s);

	in_start = b->in_pos;
	out_start = b->out_pos;
	ret = dec_main(s, b);

	if (DEC_IS_SINGLE(s->mode)) {
		if (ret == XZ_OK)
			ret = b->in_pos == b->in_size
					? XZ_DATA_ERROR : XZ_BUF_ERROR;

		if (ret != XZ_STREAM_END) {
			b->in_pos = in_start;
			b->out_pos = out_start;
		}

	} else if (ret == XZ_OK && in_start == b->in_pos
			&& out_start == b->out_pos) {
		if (s->allow_buf_error)
			ret = XZ_BUF_ERROR;

		s->allow_buf_error = true;
	} else {
		s->allow_buf_error = false;
	}

	return ret;
}
squashfs_xz_uncompressxz_dec_run->dec_main
static enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b)
{
    while(true)
/*squashfs_xz_uncompress中有介绍,第一次解压时s->sequence=SEQ_STREAM_HEADER*/
          switch(s->sequence){
/* 解压 STREAM_HEADER_SIZE=12字节的头信息*/
          case SEQ_STREAM_HEADER:
/*把STREAM_HEADER_SIZE=12字节的数据copy到xz_dec->temp.buf上*/
			if (!fill_temp(s, b))
				return XZ_OK;

			/*
			 * If dec_stream_header() returns
			 * XZ_UNSUPPORTED_CHECK, it is still possible
			 * to continue decoding if working in multi-call
			 * mode. Thus, update s->sequence before calling
			 * dec_stream_header().
			 */
			s->sequence = SEQ_BLOCK_START;

			ret = dec_stream_header(s);
			if (ret != XZ_OK)
				return ret;
/* 按顺序依次解压 不依次介绍*/ 
         case..
          }
}

【总结】

本文以squashfs文件系统为例,简单介绍了下linux系统中xz的解压缩过程。撰写本文主要目的是作为另一篇博文: squashfs文件的读取过程 的后续.


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux文件查找与压缩干货 云计算干货 Linux系统配置及服务管理文件查找 文件查找 简介 which :命令查找 find: 文件查找,针对文件名 locate:文件查找,依赖数据库 一、命令文件查找 一、查找ls 命令的位置 # which ls //从PATH环境变量 或者 # whereis vim 二、任意文件 find 语法 find [path...] [options] [expression] [action] 命令 路径 选项 表达式 动作 ①按文件名: [root@qianfeng ~]# find /etc -name "hosts" [root@qianfeng ~]# find /etc -iname "hosts" find /etc -iname "HOSTS" -i忽略大小写 i可以忽略大小写来查找 [root@qianfeng ~]# find /etc -iname "hos*" 心中有爱(i),什么都找得到 结果输出 /etc/hosts 文件查找成功 ②按文件大小: [root@qianfeng ~]# find /etc -size +5M 文件>5M 这里“size”是大小意思 [root@qianfeng ~]# find /etc -size 5M 文件=5M [root@qianfeng ~]# find /etc -size -5M 文件<5M ③指定查找的目录深度: [root@qianfeng ~]# find / -maxdepth 3 -a -name "ifcfg-en*" 这里maxdepth是深度 后面的数字是几级目录 [root@qianfeng ~]# find / -maxdepth 4 -a -name "ifcfg-en*" ④按文件属主、属组找: [root@qianfeng ~]# find /home -user aaa // 属主是aaa的文件 [root@qianfeng ~]# find /home -group hr // 属组是hr组的文件 ⑤按文件类型: [root@qianfeng ~]# find /tmp -type f f普通文件 文件type关键词 [root@qianfeng ~]# find /dev -type b b块设备文件 ⑥按文件权限: [root@qianfeng ~]# find . -perm 644 -ls 关键词perm -ls 是find的动作之一,精确权限 ⑦找到后处理的动作 ACTIONS: 找到后删除 # find /etc -name "qwer.txt" -delete 找到后复制 # find /etc -name "ifcfg*" -ok cp -rvf {} /tmp \; cp -rvf {} /tmp 对比cp /etc/passwd /tmp/etc1 找到的文件 连接符 前面找到的文件 结束符 文件打包及压缩 tar命令是Unix/Linux系统中备份文件的可靠方法, 几乎可以工作于任何环境中,它的使用权限是所有用户。 建议针对目录 打包,压缩 语法:tar 选项 压缩包名称 源文件 ===打包,压缩=== # tar -cf etc.tar /etc # tar -czf etc-gzip.tar.gz /etc/ //z是gzip # tar -cjf etc-bzip.tar.bz /etc/ //j是bzip # tar -cJf etc-xzip.tar.xz /etc/ //J是xzip 观察三个包的体积。 # ll -h etc* -rw-r--r--. 1 root root 11M 10月 14 10:07 etc-gzip.tar.gz -rw-r--r--. 1 root root 8.9M 10月 14 10:08 etc-bzip.tar.bz -rw-r--r--. 1 root root 7.6M 10月 14 10:08 etc-xzip.tar.xz 压缩速度和压缩体积成反比。 解压,解包 查看,并没有解压 # tar -tf etc.tar //t查看f文件名 解压缩 # tar xf etc3.tar.xz # tar -xvf etc2.tar.bz2 -C /tmp //-C重定向到//tmp目录
Winrar for Android 中文版。这是一款基于安卓平台手机端的解压缩工具。 Winrar for Android 中文版 Winrar for Android 是来自 RARLAB 的官方版本,是 PC 端 winrar 的姐妹,支持创建RAR和ZIP压缩文件,能够轻松解压 RAR, RAR5, ZIP, TAR, GZ, BZ2, XZ, 7z, ISO, ARJ 等格式的文件,是手机端不可多得的神器! Winrar for Android 中文版主要功能: – 支持创建RAR和ZIP压缩文件以及解压RAR, RAR5, ZIP, TAR, GZ, BZ2, XZ, 7z, ISO, ARJ等格式的文件 – 支持修复 ZIP和RAR 压缩文件,支持进行基准测试(基准测试能够测试数据处理的速度,检查错误等) – 基准测试与WinRAR兼容,请使用相同的版本在不同的设备之间进行数据测试比较,不同版本数据会相差很大 – 支持恢复记录,恢复卷,固实压缩,多核压缩,设置加密,设置配置文件,设置默认配置,重新组织配置文件 – 支持压缩后删除源文件,设置压缩文件名称,可选:标准,最快,较快,较好,最好等压缩方式,自定义字典大小 – 可设置分卷压缩文件大小,以及恢复分卷数量,支持创建每个压缩卷后自动暂停,支持显示压缩时间,显示名称 – 支持设置压缩文件名称掩码,创建压缩文件时附加当前日期字符串到压缩文件名,常用于每日备份. – 内置帮助内容,可自行阅读。支持删除压缩文件中的文件,支持创建单独压缩,自定义解压目录等 – 支持长时间操作后发出哔声提醒,自定义启动文件夹,可按照习惯排序文件夹,设置压缩文件优先
压缩和解压缩是帮助增强用户体验的必不可少的工具。 过去,您必须使用 PC 下载大文件,解压缩并复制到手机。 现在,您可以直接在智能手机上执行整个过程。 Zarchiver Pro 是可以满足您所有要求的完美应用程序。 如果您定期使用 OBB 文件安装 Android 游戏,则 Zarchiver Pro 绝对是必不可少的应用程序。 Android 解压缩工具 ZArchiver Pro 中文版Android 解压缩工具 ZArchiver Pro 中文版 ZArchiver Pro 的优点: – 白色与黑暗主题; – 密码存储; – 存档中的图像预览; – 编辑档案中的文件(请参见注释); ZArchiver Pro 可让您: 创建以下归档类型:7z(7zip),zip,bzip2(bz2),gzip(gz),XZ,tar; 解压缩以下存档类型:7z(7zip),zip,rar,rar5,bzip2,gzip,XZ,iso,tar,arj,cab,lzh,lha,lzma,xar,tgz,tbz,Z,deb,rpm,zipx ,mtz,chm,dmg,cpio,cramfs,img(fat,ntfs,ubf),wim,ecm,arc(freearc); 查看档案内容:7z(7zip),zip,rar,rar5,bzip2,gzip,XZ,iso,tar,arj,cab,lzh,lha,lzma,xar,tgz,tbz,Z,deb,rpm,zipx,mtz ,chm,dmg,cpio,cramfs,img(fat,ntfs,ubf),wim,ecm,arc(freearc); 创建和解压缩受密码保护的档案; 编辑档案:向档案中添加文件或从档案中删除文件(zip,7zip,tar,apk,mtz); 创建和解压缩多部分档案:7z,rar(仅解压缩); 部分存档解压缩; 打开压缩文件; 从邮件应用程序中打开一个存档文件; 提取分割的档案:7z,zip 和 rar(7z.001,zip.001,part1.rar,z01); 主要特征: –多线程支持(对多核处理器有用); –文件名中支持 UTF-8/UTF-16。允许您在文件名中使用国家符号; –无需启用多选模式。您可以通过单击文件名左侧的图标来选择文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值