From 62249180ee80494fc74ea3731af9bd52a78d1e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20Br=C3=BCckl?= Date: Wed, 17 Jun 2020 21:40:59 +0200 Subject: [PATCH] Handle header encrypted multi-volume 7zip archives Keep skipping volumes until the offset is reached and proceed with is7zip_mhe() to detect header encryption as before. This fixes github issue #92, reported by Ski-lleR. --- src/7zip.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/src/7zip.c b/src/7zip.c index 54ceb956..71bba0cd 100644 --- a/src/7zip.c +++ b/src/7zip.c @@ -17,6 +17,7 @@ */ #include +#include #include "7zip.h" #include "gzip_et_al.h" #include "interface.h" @@ -29,9 +30,67 @@ static gboolean data_line, encrypted, last_line; -static void xa_7zip_seek_position (GIOChannel *file, gint64 offset, GSeekType type) +static void xa_7zip_seek_position (const gchar *filename, GIOChannel **file, gint64 offset, GSeekType type) { - g_io_channel_seek_position(file, offset, type, NULL); + gchar byte; + + g_io_channel_seek_position(*file, offset, type, NULL); + + /* check whether it's a volume. i.e. whether offset is beyond the end of the file */ + if (g_io_channel_read_chars(*file, &byte, sizeof(byte), NULL, NULL) == G_IO_STATUS_NORMAL) + /* doesn't seem so - back to requested position */ + g_io_channel_seek_position(*file, -(gint64) sizeof(byte), G_SEEK_CUR, NULL); + else /* find the volume the offset is pointing to */ + { + guint64 position, volsizes = 0; + gchar *fvname; + size_t ext; + guint i; + GStatBuf st; + GIOChannel *fnew; + + if (!g_str_has_suffix(filename, ".001")) + return; + + position = 12 + 8 + (guint64) offset; // absolute position + + fvname = g_strdup(filename); + ext = strlen(fvname) - 4; + + /* check volumes ... */ + for (i = 1; i < 1000; i++) + { + fvname[ext] = 0; + sprintf(fvname, "%s.%03u", fvname, i); + + if (!g_file_test(fvname, G_FILE_TEST_EXISTS) || (g_stat(fvname, &st) != 0)) + break; + + volsizes += (guint64) st.st_size; + + /* ... up to the one we're looking for */ + if (volsizes > position) + { + fnew = g_io_channel_new_file(fvname, "r", NULL); + + if (!fnew) + break; + + /* switch to volume */ + + g_io_channel_shutdown(*file, FALSE, NULL); + + *file = fnew; + + g_io_channel_set_encoding(*file, NULL, NULL); + g_io_channel_seek_position(*file, position - (volsizes - (guint64) st.st_size), G_SEEK_SET, NULL); + + break; + } + } + + g_free(fvname); + } } static void xa_7zip_uint64_skip (GIOChannel *file) @@ -78,6 +137,7 @@ gboolean is7zip_mhe (const gchar *filename) if (file) { g_io_channel_set_encoding(file, NULL, NULL); + g_io_channel_set_buffered(file, FALSE); /* skip signature, version and header CRC32 */ g_io_channel_seek_position(file, 12, G_SEEK_SET, NULL); @@ -90,7 +150,7 @@ gboolean is7zip_mhe (const gchar *filename) } /* skip next header size and CRC32 */ - xa_7zip_seek_position(file, 12 + offset, G_SEEK_CUR); + xa_7zip_seek_position(filename, &file, 12 + offset, G_SEEK_CUR); /* header info */ g_io_channel_read_chars(file, &byte, sizeof(byte), NULL, NULL);