Пример #1
0
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)
        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));
            int r = archive_read_data(p->mpa->arch, buffer, size);
            if (r < 0) {
                MP_ERR(s, "%s\n", archive_error_string(p->mpa->arch));
                return -1;
            }
            s->pos += r;
        }
    }
    return 1;
}
Пример #2
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;
}
Пример #3
0
static int Seek(access_t *p_access, uint64_t i_pos)
{
    access_sys_t *p_sys = p_access->p_sys;

    if (!p_sys->b_seekable)
        return VLC_EGENERIC;

    int64_t i_ret = archive_seek_data(p_sys->p_archive, i_pos, SEEK_SET);
    return ( i_ret < ARCHIVE_OK ) ? VLC_EGENERIC : VLC_SUCCESS;
}
Пример #4
0
static int archive_seek_subentry( private_sys_t* p_sys, char const* psz_subentry )
{
    libarchive_t* p_arc = p_sys->p_archive;

    struct archive_entry* entry;
    int archive_status;

    while( !( archive_status = archive_read_next_header( p_arc, &entry ) ) )
    {
        char const* entry_path = archive_entry_pathname( entry );

        if( strcmp( entry_path, psz_subentry ) == 0 )
        {
            p_sys->p_entry = archive_entry_clone( entry );

            if( unlikely( !p_sys->p_entry ) )
                return VLC_ENOMEM;

            break;
        }

        archive_read_data_skip( p_arc );
    }

    switch( archive_status )
    {
        case ARCHIVE_WARN:
            msg_Warn( p_sys->p_obj,
              "libarchive: %s", archive_error_string( p_arc ) );
            /* fall through */
        case ARCHIVE_EOF:
        case ARCHIVE_FATAL:
        case ARCHIVE_RETRY:
            archive_set_error( p_arc, ARCHIVE_FATAL,
                "archive does not contain >>> %s <<<", psz_subentry );

            return VLC_EGENERIC;
    }

    /* check if seeking is supported */

    if( p_sys->b_seekable_source )
    {
        if( archive_seek_data( p_sys->p_archive, 0, SEEK_CUR ) >= 0 )
            p_sys->b_seekable_archive = true;
    }

    return VLC_SUCCESS;
}
Пример #5
0
static int Seek( stream_extractor_t* p_extractor, uint64_t i_req )
{
    private_sys_t* p_sys = p_extractor->p_sys;

    if( !p_sys->p_entry || !p_sys->b_seekable_source )
        return VLC_EGENERIC;

    if( archive_entry_size_is_set( p_sys->p_entry ) &&
        (uint64_t)archive_entry_size( p_sys->p_entry ) <= i_req )
    {
        p_sys->b_eof = true;
        return VLC_SUCCESS;
    }

    p_sys->b_eof = false;

    if( !p_sys->b_seekable_archive || p_sys->b_dead
      || archive_seek_data( p_sys->p_archive, i_req, SEEK_SET ) < 0 )
    {
        msg_Dbg( p_extractor,
            "intrinsic seek failed: '%s' (falling back to dumb seek)",
            archive_error_string( p_sys->p_archive ) );

        uint64_t i_skip = i_req - p_sys->i_offset;

        /* RECREATE LIBARCHIVE HANDLE IF WE ARE SEEKING BACKWARDS */

        if( i_req < p_sys->i_offset )
        {
            if( archive_extractor_reset( p_extractor ) )
            {
                msg_Err( p_extractor, "unable to reset libarchive handle" );
                return VLC_EGENERIC;
            }

            i_skip = i_req;
        }

        if( archive_skip_decompressed( p_extractor, i_skip ) )
            msg_Dbg( p_extractor, "failed to skip to seek position" );
    }

    p_sys->i_offset = i_req;
    return VLC_SUCCESS;
}
Пример #6
0
int AccessOpen(vlc_object_t *p_object)
{
    access_t *p_access = (access_t*)p_object;
    const char *sep = strchr(p_access->psz_location, ARCHIVE_SEP_CHAR);
    if (sep == NULL)
        return VLC_EGENERIC;

    char *psz_base = strdup(p_access->psz_location);
    if (unlikely(psz_base == NULL))
        return VLC_ENOMEM;

    char *psz_name = psz_base + (sep - p_access->psz_location);
    *(psz_name++) = '\0';

    if (decode_URI(psz_base) == NULL)
    {
        free(psz_base);
        return VLC_EGENERIC;
    }

    access_sys_t *p_sys = p_access->p_sys = calloc(1, sizeof(access_sys_t));
    p_sys->p_archive = archive_read_new();
    if (!p_sys->p_archive)
    {
        msg_Err(p_access, "can't create libarchive instance: %s",
                archive_error_string(p_sys->p_archive));
        free(psz_base);
        goto error;
    }

    EnableArchiveFormats(p_sys->p_archive);

    /* Set up the switch callback for multiple volumes handling */
    archive_read_set_switch_callback(p_sys->p_archive, SwitchCallback);

    /* !Warn: sucks because libarchive can't guess format without reading 1st header
     *        and it can't tell either if volumes are missing neither set following
     *        volumes after the first Open().
     *        We need to know volumes uri in advance then :/
     */

    /* Try to list existing volumes */
    char **ppsz_files = NULL;
    unsigned int i_files = 0;
    FindVolumes(p_access, p_sys->p_archive, psz_base, &ppsz_files, &i_files);

    p_sys->i_callback_data = 1 + i_files;
    p_sys->p_callback_data = malloc(sizeof(callback_data_t) * p_sys->i_callback_data);
    if (!p_sys->p_callback_data)
    {
        for(unsigned int i=0; i<i_files; i++)
            free(ppsz_files[i]);
        free(ppsz_files);
        free(psz_base);
        AccessClose(p_object);
        return VLC_ENOMEM;
    }

    /* set up our callback struct for our main uri */
    p_sys->p_callback_data[0].psz_uri = psz_base;
    p_sys->p_callback_data[0].p_access = p_access;
    archive_read_append_callback_data(p_sys->p_archive, &p_sys->p_callback_data[0]);

    /* and register other volumes */
    for(unsigned int i=0; i<i_files; i++)
    {
        p_sys->p_callback_data[1+i].psz_uri = ppsz_files[i];
        p_sys->p_callback_data[1+i].p_access = p_access;
        archive_read_append_callback_data(p_sys->p_archive, &p_sys->p_callback_data[1+i]);
    }
    free(ppsz_files);

    if (archive_read_open2(p_sys->p_archive, &p_sys->p_callback_data[0],
                           OpenCallback, ReadCallback, SkipCallback, CloseCallback) != ARCHIVE_OK)
    {
        msg_Err(p_access, "can't open archive: %s",
                archive_error_string(p_sys->p_archive));
        AccessClose(p_object);
        return VLC_EGENERIC;
    }

    bool b_present = false;
    while(archive_read_next_header(p_sys->p_archive, &p_sys->p_entry) == ARCHIVE_OK)
    {
        if (!strcmp(archive_entry_pathname(p_sys->p_entry), psz_name))
        {
            b_present = true;
            break;
        }
        msg_Dbg(p_access, "skipping entry %s != %s", archive_entry_pathname(p_sys->p_entry), psz_name);
    }

    if (!b_present)
    {
        msg_Err(p_access, "entry '%s' not found in archive", psz_name);
        /* entry not found */
        goto error;
    }

    msg_Dbg(p_access, "reading entry %s %"PRId64, archive_entry_pathname(p_sys->p_entry),
                                                  archive_entry_size(p_sys->p_entry));

    /* try to guess if it is seekable or not (does not depend on backend) */
    p_sys->b_seekable = (archive_seek_data(p_sys->p_archive, 0, SEEK_SET) >= 0);

    p_access->pf_read    = Read;
    p_access->pf_block   = NULL; /* libarchive's zerocopy keeps owning block :/ */
    p_access->pf_control = Control;
    p_access->pf_seek    = Seek;

    access_InitFields(p_access);

    return VLC_SUCCESS;

error:
    AccessClose(p_object);
    return VLC_EGENERIC;
}