xz压缩文件的解压缩过程
生活随笔
收集整理的這篇文章主要介紹了
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_runXZ_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文件的讀取過程?的后續.
總結
以上是生活随笔為你收集整理的xz压缩文件的解压缩过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 枫叶防注入程序漏洞
- 下一篇: pygame(十五)拼图游戏