From: Duncan Horn <40036384+dunhor@users.noreply.github.com> Date: Thu, 20 Jun 2024 03:15:13 +0000 (-0700) Subject: Fix issue when skipping first file in 7zip archive that is a multiple of 65536 bytes... X-Git-Tag: v3.7.5~32 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a578fb345a0221b000fb6c2857ae7d790febe94b;p=thirdparty%2Flibarchive.git Fix issue when skipping first file in 7zip archive that is a multiple of 65536 bytes (#2245) We noticed an issue where we had an archive that, if you skipped the first entry and tried to extract the second, you'd get a failure saying `Truncated 7-Zip file body`. Turns out that this is because the first file in the archive is a multiple of 65,536 bytes (the size of the uncompressed buffer) and therefore after `read_stream` skipped all of the first file, `uncompressed_buffer_bytes_remaining` was set to zero (because all data was consumed) and then it calls `get_uncompressed_data` with `minimum` set to zero. This then saw that `minimum > zip->uncompressed_buffer_bytes_remaining` evaluated to false, causing us to read zero bytes, which got interpreted as a truncated archive. The fix here is simple: we now always call `extract_pack_stream` when `uncompressed_buffer_bytes_remaining` is zero before exiting the skipping loop. --- diff --git a/Makefile.am b/Makefile.am index 7560b14fe..532b367c2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -789,6 +789,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_7zip_encryption.7z.uu \ libarchive/test/test_read_format_7zip_encryption_header.7z.uu \ libarchive/test/test_read_format_7zip_encryption_partially.7z.uu \ + libarchive/test/test_read_format_7zip_extract_second.7z.uu \ libarchive/test/test_read_format_7zip_lzma1.7z.uu \ libarchive/test/test_read_format_7zip_lzma1_2.7z.uu \ libarchive/test/test_read_format_7zip_lzma1_lzma2.7z.uu \ diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c index 634521d95..e322808e7 100644 --- a/libarchive/archive_read_support_format_7zip.c +++ b/libarchive/archive_read_support_format_7zip.c @@ -3462,7 +3462,7 @@ read_stream(struct archive_read *a, const void **buff, size_t size, /* * Skip the bytes we already has skipped in skip_stream(). */ - while (skip_bytes) { + while (1) { ssize_t skipped; if (zip->uncompressed_buffer_bytes_remaining == 0) { @@ -3482,6 +3482,10 @@ read_stream(struct archive_read *a, const void **buff, size_t size, return (ARCHIVE_FATAL); } } + + if (!skip_bytes) + break; + skipped = get_uncompressed_data( a, buff, (size_t)skip_bytes, 0); if (skipped < 0) diff --git a/libarchive/test/test_read_format_7zip.c b/libarchive/test/test_read_format_7zip.c index cff82f2c3..bb47be668 100644 --- a/libarchive/test/test_read_format_7zip.c +++ b/libarchive/test/test_read_format_7zip.c @@ -1257,5 +1257,47 @@ DEFINE_TEST(test_read_format_7zip_win_attrib) assertEqualString("system", archive_entry_fflags_text(ae)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_extract_second) +{ + struct archive *a; + char buffer[256]; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping( + "7zip:lzma decoding is not supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + return; + } + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + + /* + * The test archive has two files: first.txt which is a 65,536 file (the + * size of the uncompressed buffer), and second.txt which has contents + * we will validate. This test ensures we can skip first.txt and still + * be able to read the contents of second.txt + */ + const char *refname = "test_read_format_7zip_extract_second.7z"; + extract_reference_file(refname); + + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + struct archive_entry *ae; + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("first.txt", archive_entry_pathname(ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("second.txt", archive_entry_pathname(ae)); + + assertEqualInt(23, archive_read_data(a, buffer, sizeof(buffer))); + assertEqualMem("This is from second.txt", buffer, 23); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } diff --git a/libarchive/test/test_read_format_7zip_extract_second.7z.uu b/libarchive/test/test_read_format_7zip_extract_second.7z.uu new file mode 100644 index 000000000..aa3d6e235 --- /dev/null +++ b/libarchive/test/test_read_format_7zip_extract_second.7z.uu @@ -0,0 +1,11 @@ +begin 644 test_read_format_7zip_extract_second.7z +M-WJ\KR<<``-N%=VX!@$````````B`````````*R\U.<`&`Q"DFIGO`[1,RO\ +MN,RA7-QU1L&_]O_/$0MMLIEBUR3'BDX@M2C-5'VG./-4,5@W3Q@*__^7_,[H +MEO`DB'[ZI>@H2_E>/W.2G$$.P01-X!YN5";SS[3#7Z4Q1G/EF.0'^D*[S8&8 +M[FV9DYX7,SA%^.Q\'?__P!@`````@3,'K@_4WV/Q0A7VLXG$X?GH4=5W^`UM +M$N_EX$)LE*?K$W5?WLP:X0T[Q%V^?A!0E\VZRBB,)(MO`C`LO[O!3(1YL)<: +MJ."`';WU;>GP5',%Z=6?*/H9*Z)&\*!2^,RV`R30UOBH8+5.;;2IKF +M0W://&'?"L?0L2!)`*]F30B0&/_'<4``%P9Z`0F`C``'"P$``2,#`0$%70`` +-@``,@*@*`6]FB2D````` +` +end