static int reopen_archive(stream_t *s) { struct priv *p = s->priv; mp_archive_free(p->mpa); p->mpa = mp_archive_new(s->log, p->src, MP_ARCHIVE_FLAG_UNSAFE); if (!p->mpa) return STREAM_ERROR; // Follows the same logic as demux_libarchive.c. struct mp_archive *mpa = p->mpa; while (mp_archive_next_entry(mpa)) { if (strcmp(p->entry_name, mpa->entry_filename) == 0) { locale_t oldlocale = uselocale(mpa->locale); p->entry_size = -1; if (archive_entry_size_is_set(mpa->entry)) p->entry_size = archive_entry_size(mpa->entry); uselocale(oldlocale); return STREAM_OK; } } mp_archive_free(p->mpa); p->mpa = NULL; MP_ERR(s, "archive entry not found. '%s'\n", p->entry_name); return STREAM_ERROR; }
struct mp_archive *mp_archive_new(struct mp_log *log, struct stream *src, int flags) { struct mp_archive *mpa = talloc_zero(NULL, struct mp_archive); mpa->log = log; mpa->locale = newlocale(LC_ALL_MASK, "C.UTF-8", (locale_t)0); if (!mpa->locale) goto err; mpa->arch = archive_read_new(); mpa->primary_src = src; if (!mpa->arch) goto err; // first volume is the primary streame if (!add_volume(log ,mpa, src, src->url)) goto err; // try to open other volumes char** volumes = find_volumes(src); for (int i = 0; volumes[i]; i++) { if (!add_volume(log, mpa, NULL, volumes[i])) { talloc_free(volumes); goto err; } } talloc_free(volumes); locale_t oldlocale = uselocale(mpa->locale); archive_read_support_format_7zip(mpa->arch); archive_read_support_format_iso9660(mpa->arch); archive_read_support_format_rar(mpa->arch); archive_read_support_format_zip(mpa->arch); archive_read_support_filter_bzip2(mpa->arch); archive_read_support_filter_gzip(mpa->arch); archive_read_support_filter_xz(mpa->arch); if (flags & MP_ARCHIVE_FLAG_UNSAFE) { archive_read_support_format_gnutar(mpa->arch); archive_read_support_format_tar(mpa->arch); } archive_read_set_read_callback(mpa->arch, read_cb); archive_read_set_skip_callback(mpa->arch, skip_cb); archive_read_set_switch_callback(mpa->arch, switch_cb); archive_read_set_open_callback(mpa->arch, open_cb); archive_read_set_close_callback(mpa->arch, close_cb); if (mpa->primary_src->seekable) archive_read_set_seek_callback(mpa->arch, seek_cb); bool fail = archive_read_open1(mpa->arch) < ARCHIVE_OK; uselocale(oldlocale); if (fail) goto err; return mpa; err: mp_archive_free(mpa); return NULL; }
static int open_file(struct demuxer *demuxer, enum demux_check check) { if (stream_get_size(demuxer->stream) == 0) return -1; int flags = 0; if (check <= DEMUX_CHECK_REQUEST) flags |= MP_ARCHIVE_FLAG_UNSAFE; struct mp_archive *mpa = mp_archive_new(demuxer->log, demuxer->stream, flags); if (!mpa) return -1; struct playlist *pl = talloc_zero(demuxer, struct playlist); demuxer->playlist = pl; // make it load archive:// pl->disable_safety = true; char *prefix = mp_url_escape(mpa, demuxer->stream->url, "~|"); char **files = NULL; int num_files = 0; for (;;) { struct archive_entry *entry; int r = archive_read_next_header(mpa->arch, &entry); if (r == ARCHIVE_EOF) break; if (r < ARCHIVE_OK) MP_ERR(demuxer, "libarchive: %s\n", archive_error_string(mpa->arch)); if (r < ARCHIVE_WARN) break; if (archive_entry_filetype(entry) != AE_IFREG) continue; const char *fn = archive_entry_pathname(entry); // Some archives may have no filenames. if (!fn) fn = talloc_asprintf(mpa, "mpv_unknown#%d\n", num_files); // stream_libarchive.c does the real work char *f = talloc_asprintf(mpa, "archive://%s|%s", prefix, fn); MP_TARRAY_APPEND(mpa, files, num_files, f); } if (files) qsort(files, num_files, sizeof(files[0]), cmp_filename); for (int n = 0; n < num_files; n++) playlist_add_file(pl, files[n]); demuxer->filetype = "archive"; demuxer->fully_read = true; mp_archive_free(mpa); return 0; }
static int archive_entry_seek(stream_t *s, int64_t newpos) { struct priv *p = s->priv; if (p->mpa && !p->broken_seek) { locale_t oldlocale = uselocale(p->mpa->locale); int r = archive_seek_data(p->mpa->arch, newpos, SEEK_SET); uselocale(oldlocale); if (r >= 0) return 1; MP_WARN(s, "possibly unsupported seeking - switching to reopening\n"); p->broken_seek = true; if (reopen_archive(s) < STREAM_OK) return -1; } // libarchive can't seek in most formats. if (newpos < s->pos) { // Hack seeking backwards into working by reopening the archive and // starting over. MP_VERBOSE(s, "trying to reopen archive for performing seek\n"); if (reopen_archive(s) < STREAM_OK) return -1; s->pos = 0; } if (newpos > s->pos) { // For seeking forwards, just keep reading data (there's no libarchive // skip function either). char buffer[4096]; while (newpos > s->pos) { if (mp_cancel_test(s->cancel)) return -1; int size = MPMIN(newpos - s->pos, sizeof(buffer)); locale_t oldlocale = uselocale(p->mpa->locale); int r = archive_read_data(p->mpa->arch, buffer, size); if (r <= 0) { if (r == 0 && newpos > p->entry_size) { MP_ERR(s, "demuxer trying to seek beyond end of archive " "entry\n"); } else if (r == 0) { MP_ERR(s, "end of archive entry reached while seeking\n"); } else { MP_ERR(s, "%s\n", archive_error_string(p->mpa->arch)); } uselocale(oldlocale); if (mp_archive_check_fatal(p->mpa, r)) { mp_archive_free(p->mpa); p->mpa = NULL; } return -1; } uselocale(oldlocale); s->pos += r; } } return 1; }
static int archive_entry_fill_buffer(stream_t *s, char *buffer, int max_len) { struct priv *p = s->priv; if (!p->mpa) return 0; locale_t oldlocale = uselocale(p->mpa->locale); int r = archive_read_data(p->mpa->arch, buffer, max_len); 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; } } uselocale(oldlocale); return r; }
static void archive_entry_close(stream_t *s) { struct priv *p = s->priv; mp_archive_free(p->mpa); free_stream(p->src); }