xz: Use lzma_file_info_decoder() for --list.

This commit is contained in:
Lasse Collin 2017-04-24 19:48:23 +03:00
parent e353d0b1cc
commit 8269782283
1 changed files with 50 additions and 216 deletions

View File

@ -143,9 +143,6 @@ xz_ver_to_str(uint32_t ver)
/// ///
/// \return On success, false is returned. On error, true is returned. /// \return On success, false is returned. On error, true is returned.
/// ///
// TODO: This function is pretty big. liblzma should have a function that
// takes a callback function to parse the Index(es) from a .xz file to make
// it easy for applications.
static bool static bool
parse_indexes(xz_file_info *xfi, file_pair *pair) parse_indexes(xz_file_info *xfi, file_pair *pair)
{ {
@ -161,238 +158,75 @@ parse_indexes(xz_file_info *xfi, file_pair *pair)
} }
io_buf buf; io_buf buf;
lzma_stream_flags header_flags;
lzma_stream_flags footer_flags;
lzma_ret ret;
// lzma_stream for the Index decoder
lzma_stream strm = LZMA_STREAM_INIT; lzma_stream strm = LZMA_STREAM_INIT;
lzma_index *idx = NULL;
// All Indexes decoded so far lzma_ret ret = lzma_file_info_decoder(&strm, &idx,
lzma_index *combined_index = NULL; hardware_memlimit_get(MODE_LIST),
(uint64_t)(pair->src_st.st_size));
// The Index currently being decoded if (ret != LZMA_OK) {
lzma_index *this_index = NULL; message_error("%s: %s", pair->src_name, message_strm(ret));
return true;
// Current position in the file. We parse the file backwards so
// initialize it to point to the end of the file.
off_t pos = pair->src_st.st_size;
// Each loop iteration decodes one Index.
do {
// Check that there is enough data left to contain at least
// the Stream Header and Stream Footer. This check cannot
// fail in the first pass of this loop.
if (pos < 2 * LZMA_STREAM_HEADER_SIZE) {
message_error("%s: %s", pair->src_name,
message_strm(LZMA_DATA_ERROR));
goto error;
} }
pos -= LZMA_STREAM_HEADER_SIZE;
lzma_vli stream_padding = 0;
// Locate the Stream Footer. There may be Stream Padding which
// we must skip when reading backwards.
while (true) { while (true) {
if (pos < LZMA_STREAM_HEADER_SIZE) { if (strm.avail_in == 0) {
message_error("%s: %s", pair->src_name,
message_strm(
LZMA_DATA_ERROR));
goto error;
}
if (io_pread(pair, &buf,
LZMA_STREAM_HEADER_SIZE, pos))
goto error;
// Stream Padding is always a multiple of four bytes.
int i = 2;
if (buf.u32[i] != 0)
break;
// To avoid calling io_pread() for every four bytes
// of Stream Padding, take advantage that we read
// 12 bytes (LZMA_STREAM_HEADER_SIZE) already and
// check them too before calling io_pread() again.
do {
stream_padding += 4;
pos -= 4;
--i;
} while (i >= 0 && buf.u32[i] == 0);
}
// Decode the Stream Footer.
ret = lzma_stream_footer_decode(&footer_flags, buf.u8);
if (ret != LZMA_OK) {
message_error("%s: %s", pair->src_name,
message_strm(ret));
goto error;
}
// Check that the Stream Footer doesn't specify something
// that we don't support. This can only happen if the xz
// version is older than liblzma and liblzma supports
// something new.
//
// It is enough to check Stream Footer. Stream Header must
// match when it is compared against Stream Footer with
// lzma_stream_flags_compare().
if (footer_flags.version != 0) {
message_error("%s: %s", pair->src_name,
message_strm(LZMA_OPTIONS_ERROR));
goto error;
}
// Check that the size of the Index field looks sane.
lzma_vli index_size = footer_flags.backward_size;
if ((lzma_vli)(pos) < index_size + LZMA_STREAM_HEADER_SIZE) {
message_error("%s: %s", pair->src_name,
message_strm(LZMA_DATA_ERROR));
goto error;
}
// Set pos to the beginning of the Index.
pos -= index_size;
// See how much memory we can use for decoding this Index.
uint64_t memlimit = hardware_memlimit_get(MODE_LIST);
uint64_t memused = 0;
if (combined_index != NULL) {
memused = lzma_index_memused(combined_index);
if (memused > memlimit)
message_bug();
memlimit -= memused;
}
// Decode the Index.
ret = lzma_index_decoder(&strm, &this_index, memlimit);
if (ret != LZMA_OK) {
message_error("%s: %s", pair->src_name,
message_strm(ret));
goto error;
}
do {
// Don't give the decoder more input than the
// Index size.
strm.avail_in = my_min(IO_BUFFER_SIZE, index_size);
if (io_pread(pair, &buf, strm.avail_in, pos))
goto error;
pos += strm.avail_in;
index_size -= strm.avail_in;
strm.next_in = buf.u8; strm.next_in = buf.u8;
strm.avail_in = io_read(pair, &buf, IO_BUFFER_SIZE);
if (strm.avail_in == SIZE_MAX)
goto error;
}
ret = lzma_code(&strm, LZMA_RUN); ret = lzma_code(&strm, LZMA_RUN);
} while (ret == LZMA_OK); switch (ret) {
case LZMA_OK:
break;
// If the decoding seems to be successful, check also that case LZMA_SEEK_NEEDED:
// the Index decoder consumed as much input as indicated // The cast is safe because liblzma won't ask us to
// by the Backward Size field. // seek past the known size of the input file which
if (ret == LZMA_STREAM_END) // did fit into off_t.
if (index_size != 0 || strm.avail_in != 0) assert(strm.seek_pos
ret = LZMA_DATA_ERROR; <= (uint64_t)(pair->src_st.st_size));
if (io_seek_src(pair, (off_t)(strm.seek_pos)))
goto error;
if (ret != LZMA_STREAM_END) { // avail_in must be zero so that we will read new
// LZMA_BUFFER_ERROR means that the Index decoder // input.
// would have liked more input than what the Index strm.avail_in = 0;
// size should be according to Stream Footer. break;
// The message for LZMA_DATA_ERROR makes more
// sense in that case.
if (ret == LZMA_BUF_ERROR)
ret = LZMA_DATA_ERROR;
case LZMA_STREAM_END: {
lzma_end(&strm);
xfi->idx = idx;
// Calculate xfi->stream_padding.
lzma_index_iter iter;
lzma_index_iter_init(&iter, xfi->idx);
while (!lzma_index_iter_next(&iter,
LZMA_INDEX_ITER_STREAM))
xfi->stream_padding += iter.stream.padding;
return false;
}
default:
message_error("%s: %s", pair->src_name, message_error("%s: %s", pair->src_name,
message_strm(ret)); message_strm(ret));
// If the error was too low memory usage limit, // If the error was too low memory usage limit,
// show also how much memory would have been needed. // show also how much memory would have been needed.
if (ret == LZMA_MEMLIMIT_ERROR) { if (ret == LZMA_MEMLIMIT_ERROR)
uint64_t needed = lzma_memusage(&strm); message_mem_needed(V_ERROR,
if (UINT64_MAX - needed < memused) lzma_memusage(&strm));
needed = UINT64_MAX;
else
needed += memused;
message_mem_needed(V_ERROR, needed);
}
goto error; goto error;
} }
// Decode the Stream Header and check that its Stream Flags
// match the Stream Footer.
pos -= footer_flags.backward_size + LZMA_STREAM_HEADER_SIZE;
if ((lzma_vli)(pos) < lzma_index_total_size(this_index)) {
message_error("%s: %s", pair->src_name,
message_strm(LZMA_DATA_ERROR));
goto error;
} }
pos -= lzma_index_total_size(this_index);
if (io_pread(pair, &buf, LZMA_STREAM_HEADER_SIZE, pos))
goto error;
ret = lzma_stream_header_decode(&header_flags, buf.u8);
if (ret != LZMA_OK) {
message_error("%s: %s", pair->src_name,
message_strm(ret));
goto error;
}
ret = lzma_stream_flags_compare(&header_flags, &footer_flags);
if (ret != LZMA_OK) {
message_error("%s: %s", pair->src_name,
message_strm(ret));
goto error;
}
// Store the decoded Stream Flags into this_index. This is
// needed so that we can print which Check is used in each
// Stream.
ret = lzma_index_stream_flags(this_index, &footer_flags);
if (ret != LZMA_OK)
message_bug();
// Store also the size of the Stream Padding field. It is
// needed to show the offsets of the Streams correctly.
ret = lzma_index_stream_padding(this_index, stream_padding);
if (ret != LZMA_OK)
message_bug();
if (combined_index != NULL) {
// Append the earlier decoded Indexes
// after this_index.
ret = lzma_index_cat(
this_index, combined_index, NULL);
if (ret != LZMA_OK) {
message_error("%s: %s", pair->src_name,
message_strm(ret));
goto error;
}
}
combined_index = this_index;
this_index = NULL;
xfi->stream_padding += stream_padding;
} while (pos > 0);
lzma_end(&strm);
// All OK. Make combined_index available to the caller.
xfi->idx = combined_index;
return false;
error: error:
// Something went wrong, free the allocated memory.
lzma_end(&strm); lzma_end(&strm);
lzma_index_end(combined_index, NULL);
lzma_index_end(this_index, NULL);
return true; return true;
} }