xz: Use lzma_file_info_decoder() for --list.
This commit is contained in:
parent
e353d0b1cc
commit
8269782283
258
src/xz/list.c
258
src/xz/list.c
|
@ -143,9 +143,6 @@ xz_ver_to_str(uint32_t ver)
|
|||
///
|
||||
/// \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
|
||||
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;
|
||||
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_index *idx = NULL;
|
||||
|
||||
// All Indexes decoded so far
|
||||
lzma_index *combined_index = NULL;
|
||||
|
||||
// The Index currently being decoded
|
||||
lzma_index *this_index = NULL;
|
||||
|
||||
// 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;
|
||||
lzma_ret ret = lzma_file_info_decoder(&strm, &idx,
|
||||
hardware_memlimit_get(MODE_LIST),
|
||||
(uint64_t)(pair->src_st.st_size));
|
||||
if (ret != LZMA_OK) {
|
||||
message_error("%s: %s", pair->src_name, message_strm(ret));
|
||||
return true;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (pos < LZMA_STREAM_HEADER_SIZE) {
|
||||
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;
|
||||
|
||||
if (strm.avail_in == 0) {
|
||||
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);
|
||||
|
||||
} while (ret == LZMA_OK);
|
||||
switch (ret) {
|
||||
case LZMA_OK:
|
||||
break;
|
||||
|
||||
// If the decoding seems to be successful, check also that
|
||||
// the Index decoder consumed as much input as indicated
|
||||
// by the Backward Size field.
|
||||
if (ret == LZMA_STREAM_END)
|
||||
if (index_size != 0 || strm.avail_in != 0)
|
||||
ret = LZMA_DATA_ERROR;
|
||||
case LZMA_SEEK_NEEDED:
|
||||
// The cast is safe because liblzma won't ask us to
|
||||
// seek past the known size of the input file which
|
||||
// did fit into off_t.
|
||||
assert(strm.seek_pos
|
||||
<= (uint64_t)(pair->src_st.st_size));
|
||||
if (io_seek_src(pair, (off_t)(strm.seek_pos)))
|
||||
goto error;
|
||||
|
||||
if (ret != LZMA_STREAM_END) {
|
||||
// LZMA_BUFFER_ERROR means that the Index decoder
|
||||
// would have liked more input than what the Index
|
||||
// size should be according to Stream Footer.
|
||||
// The message for LZMA_DATA_ERROR makes more
|
||||
// sense in that case.
|
||||
if (ret == LZMA_BUF_ERROR)
|
||||
ret = LZMA_DATA_ERROR;
|
||||
// avail_in must be zero so that we will read new
|
||||
// input.
|
||||
strm.avail_in = 0;
|
||||
break;
|
||||
|
||||
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_strm(ret));
|
||||
|
||||
// If the error was too low memory usage limit,
|
||||
// show also how much memory would have been needed.
|
||||
if (ret == LZMA_MEMLIMIT_ERROR) {
|
||||
uint64_t needed = lzma_memusage(&strm);
|
||||
if (UINT64_MAX - needed < memused)
|
||||
needed = UINT64_MAX;
|
||||
else
|
||||
needed += memused;
|
||||
|
||||
message_mem_needed(V_ERROR, needed);
|
||||
}
|
||||
if (ret == LZMA_MEMLIMIT_ERROR)
|
||||
message_mem_needed(V_ERROR,
|
||||
lzma_memusage(&strm));
|
||||
|
||||
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:
|
||||
// Something went wrong, free the allocated memory.
|
||||
lzma_end(&strm);
|
||||
lzma_index_end(combined_index, NULL);
|
||||
lzma_index_end(this_index, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue