]> granicus.if.org Git - xz/commitdiff
xz: Use lzma_file_info_decoder() for --list.
authorLasse Collin <lasse.collin@tukaani.org>
Mon, 24 Apr 2017 16:48:23 +0000 (19:48 +0300)
committerLasse Collin <lasse.collin@tukaani.org>
Mon, 24 Apr 2017 16:48:23 +0000 (19:48 +0300)
src/xz/list.c

index 449c2bc4e02fd8992a68884bc2bcf08138734bb3..c297d2e3fd1577258b43b8b1a305451ec6d1a9be 100644 (file)
@@ -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;
-                       }
+       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;
+       }
 
-                       if (io_pread(pair, &buf,
-                                       LZMA_STREAM_HEADER_SIZE, pos))
+       while (true) {
+               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;
-
-                       // 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;
-               }
+               ret = lzma_code(&strm, LZMA_RUN);
 
-               // 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;
-               }
+               switch (ret) {
+               case LZMA_OK:
+                       break;
 
-               // 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;
-               }
+               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;
 
-               // Set pos to the beginning of the Index.
-               pos -= index_size;
+                       // avail_in must be zero so that we will read new
+                       // input.
+                       strm.avail_in = 0;
+                       break;
 
-               // 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();
+               case LZMA_STREAM_END: {
+                       lzma_end(&strm);
+                       xfi->idx = idx;
 
-                       memlimit -= memused;
-               }
+                       // 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;
 
-               // 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;
+                       return false;
                }
 
-               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;
-                       ret = lzma_code(&strm, LZMA_RUN);
-
-               } while (ret == LZMA_OK);
-
-               // 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;
-
-               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;
-
+               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;
 }