mirror of
https://git.tukaani.org/xz.git
synced 2024-04-04 12:36:23 +02:00
xz: Use lzma_file_info_decoder() for --list.
This commit is contained in:
parent
e353d0b1cc
commit
8269782283
1 changed files with 50 additions and 216 deletions
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.
|
/// \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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue