summaryrefslogtreecommitdiffstats
path: root/stream/stream_libarchive.c
diff options
context:
space:
mode:
Diffstat (limited to 'stream/stream_libarchive.c')
-rw-r--r--stream/stream_libarchive.c45
1 files changed, 41 insertions, 4 deletions
diff --git a/stream/stream_libarchive.c b/stream/stream_libarchive.c
index a840de706b..b4267e6ebb 100644
--- a/stream/stream_libarchive.c
+++ b/stream/stream_libarchive.c
@@ -126,12 +126,30 @@ static int switch_cb(struct archive *arch, void *oldpriv, void *newpriv)
return open_cb(arch, newpriv);
}
-void mp_archive_free(struct mp_archive *mpa)
+static void mp_archive_close(struct mp_archive *mpa)
{
if (mpa && mpa->arch) {
archive_read_close(mpa->arch);
archive_read_free(mpa->arch);
+ mpa->arch = NULL;
}
+}
+
+// Supposedly we're not allowed to continue reading on FATAL returns. Otherwise
+// crashes and other UB is possible. Assume calling the close/free functions is
+// still ok. Return true if it was fatal and the archive was closed.
+static bool mp_archive_check_fatal(struct mp_archive *mpa, int r)
+{
+ if (r > ARCHIVE_FATAL)
+ return false;
+ MP_FATAL(mpa, "fatal error received - cllsing archive\n");
+ mp_archive_close(mpa);
+ return true;
+}
+
+void mp_archive_free(struct mp_archive *mpa)
+{
+ mp_archive_close(mpa);
talloc_free(mpa);
}
@@ -274,6 +292,9 @@ bool mp_archive_next_entry(struct mp_archive *mpa)
talloc_free(mpa->entry_filename);
mpa->entry_filename = NULL;
+ if (!mpa->arch)
+ return false;
+
while (!mp_cancel_test(mpa->primary_src->cancel)) {
struct archive_entry *entry;
int r = archive_read_next_header(mpa->arch, &entry);
@@ -283,6 +304,7 @@ bool mp_archive_next_entry(struct mp_archive *mpa)
MP_ERR(mpa, "%s\n", archive_error_string(mpa->arch));
if (r < ARCHIVE_WARN) {
MP_FATAL(mpa, "could not read archive entry\n");
+ mp_archive_check_fatal(mpa, r);
break;
}
if (archive_entry_filetype(entry) != AE_IFREG)
@@ -341,8 +363,13 @@ static int archive_entry_fill_buffer(stream_t *s, char *buffer, int max_len)
if (!p->mpa)
return 0;
int r = archive_read_data(p->mpa->arch, buffer, max_len);
- if (r < 0)
+ if (r < 0) {
MP_ERR(s, "%s\n", archive_error_string(p->mpa->arch));
+ if (mp_archive_check_fatal(p->mpa, r)) {
+ mp_archive_free(p->mpa);
+ p->mpa = NULL;
+ }
+ }
return r;
}
@@ -351,8 +378,14 @@ static int archive_entry_seek(stream_t *s, int64_t newpos)
struct priv *p = s->priv;
if (!p->mpa)
return -1;
- if (archive_seek_data(p->mpa->arch, newpos, SEEK_SET) >= 0)
+ int r = archive_seek_data(p->mpa->arch, newpos, SEEK_SET);
+ if (r >= 0)
return 1;
+ if (mp_archive_check_fatal(p->mpa, r)) {
+ mp_archive_free(p->mpa);
+ p->mpa = NULL;
+ return -1;
+ }
// libarchive can't seek in most formats.
if (newpos < s->pos) {
// Hack seeking backwards into working by reopening the archive and
@@ -371,9 +404,13 @@ static int archive_entry_seek(stream_t *s, int64_t newpos)
return -1;
int size = MPMIN(newpos - s->pos, sizeof(buffer));
- int r = archive_read_data(p->mpa->arch, buffer, size);
+ r = archive_read_data(p->mpa->arch, buffer, size);
if (r < 0) {
MP_ERR(s, "%s\n", archive_error_string(p->mpa->arch));
+ if (mp_archive_check_fatal(p->mpa, r)) {
+ mp_archive_free(p->mpa);
+ p->mpa = NULL;
+ }
return -1;
}
s->pos += r;