diff --git a/src/xz/list.c b/src/xz/list.c index 449c2bc4..c297d2e3 100644 --- a/src/xz/list.c +++ b/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; - } - - 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; + 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; + } + while (true) { + if (strm.avail_in == 0) { strm.next_in = buf.u8; - ret = lzma_code(&strm, LZMA_RUN); + strm.avail_in = io_read(pair, &buf, IO_BUFFER_SIZE); + if (strm.avail_in == SIZE_MAX) + goto error; + } - } while (ret == LZMA_OK); + ret = lzma_code(&strm, LZMA_RUN); - // 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; + switch (ret) { + case LZMA_OK: + break; - 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; + 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; + // 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; }