File-Raw-Archive

 view release on metacpan or  search on metacpan

Archive.xs  view on Meta::CPAN

    hv_stores(h, "size",     newSVuv((UV)e->size));
    hv_stores(h, "mode",     newSVuv((UV)e->mode));
    hv_stores(h, "mtime",    newSVuv((UV)e->mtime));
    hv_stores(h, "mtime_ns", newSVuv((UV)e->mtime_ns));
    hv_stores(h, "uid",      newSVuv((UV)e->uid));
    hv_stores(h, "gid",      newSVuv((UV)e->gid));
    hv_stores(h, "type",     newSViv((IV)e->type));
    hv_stores(h, "is_sparse", newSViv(e->is_sparse));
    if (e->link_target)
        hv_stores(h, "link_target",
                  newSVpvn(e->link_target, e->link_target_len));
    if (e->xattr_count > 0) {
        HV *xh = newHV();
        size_t i;
        for (i = 0; i < e->xattr_count; i++) {
            hv_store(xh, e->xattrs[i].key, (I32)e->xattrs[i].key_len,
                     newSVpvn(e->xattrs[i].value, e->xattrs[i].value_len), 0);
        }
        hv_stores(h, "xattrs", newRV_noinc((SV *)xh));
    }
    return newRV_noinc((SV *)h);
}

/* Build a meta AV from an ArchiveEntry. Slot layout: M_NAME..M_IS_SPARSE.
 * Returns RV-AV at refcount +1. Slots that aren't applicable (e.g.
 * link_target on a regular file) are left empty rather than stored as
 * undef so size() sees a missing slot and returns the default. */
static SV *
entry_to_av(pTHX_ const ArchiveEntry *e)
{
    AV *meta = newAV();
    av_extend(meta, M_SLOT_COUNT - 1);
    av_store(meta, M_NAME,
             newSVpvn(e->name ? e->name : "", e->name_len));
    av_store(meta, M_SIZE,        newSVuv((UV)e->size));
    av_store(meta, M_MODE,        newSVuv((UV)e->mode));
    av_store(meta, M_MTIME,       newSVuv((UV)e->mtime));
    av_store(meta, M_MTIME_NS,    newSVuv((UV)e->mtime_ns));
    av_store(meta, M_UID,         newSVuv((UV)e->uid));
    av_store(meta, M_GID,         newSVuv((UV)e->gid));
    av_store(meta, M_TYPE,        newSViv((IV)e->type));
    av_store(meta, M_IS_SPARSE,   newSViv(e->is_sparse));
    if (e->link_target) {
        av_store(meta, M_LINK_TARGET,
                 newSVpvn(e->link_target, e->link_target_len));
    }
    if (e->xattr_count > 0) {
        HV *xh = newHV();
        size_t i;
        for (i = 0; i < e->xattr_count; i++) {
            hv_store(xh, e->xattrs[i].key, (I32)e->xattrs[i].key_len,
                     newSVpvn(e->xattrs[i].value, e->xattrs[i].value_len), 0);
        }
        av_store(meta, M_XATTRS, newRV_noinc((SV *)xh));
    }
    return newRV_noinc((SV *)meta);
}

/* Decode a Perl entry hashref into ArchiveEntry. The returned entry
 * borrows pointers into SVs that must outlive the call - caller keeps
 * the hash alive until write_add returns. */
static void
hv_to_entry(pTHX_ HV *h, ArchiveEntry *e,
            ArchiveXattr *xattr_buf, size_t xattr_buf_n,
            size_t *xattr_used)
{
    memset(e, 0, sizeof *e);
    *xattr_used = 0;

    SV **sv = hv_fetchs(h, "name", 0);
    if (sv && *sv && SvOK(*sv)) {
        STRLEN nl;
        e->name = SvPV(*sv, nl);
        e->name_len = nl;
    }
    sv = hv_fetchs(h, "type", 0);
    if (sv && *sv && SvOK(*sv)) e->type = (int)SvIV(*sv);
    else e->type = AE_FILE;

    sv = hv_fetchs(h, "mode", 0);
    if (sv && *sv && SvOK(*sv)) e->mode = (uint32_t)SvUV(*sv);

    sv = hv_fetchs(h, "size", 0);
    if (sv && *sv && SvOK(*sv)) e->size = (uint64_t)SvUV(*sv);

    sv = hv_fetchs(h, "mtime", 0);
    if (sv && *sv && SvOK(*sv)) {
        if (SvNOKp(*sv) || (SvPOKp(*sv) && strchr(SvPV_nolen(*sv), '.'))) {
            NV nv = SvNV(*sv);
            uint64_t whole = (uint64_t)nv;
            double frac = nv - (double)whole;
            if (frac < 0) frac = 0;
            e->mtime = whole;
            e->mtime_ns = (uint32_t)(frac * 1e9 + 0.5);
            if (e->mtime_ns >= 1000000000U) {
                e->mtime++;
                e->mtime_ns -= 1000000000U;
            }
        } else {
            e->mtime = (uint64_t)SvUV(*sv);
        }
    }
    sv = hv_fetchs(h, "mtime_ns", 0);
    if (sv && *sv && SvOK(*sv)) e->mtime_ns = (uint32_t)SvUV(*sv);

    sv = hv_fetchs(h, "uid", 0);
    if (sv && *sv && SvOK(*sv)) e->uid = (uint32_t)SvUV(*sv);
    sv = hv_fetchs(h, "gid", 0);
    if (sv && *sv && SvOK(*sv)) e->gid = (uint32_t)SvUV(*sv);

    sv = hv_fetchs(h, "link_target", 0);
    if (sv && *sv && SvOK(*sv)) {
        STRLEN ll;
        e->link_target = SvPV(*sv, ll);
        e->link_target_len = ll;
    }

    sv = hv_fetchs(h, "xattrs", 0);
    if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVHV) {
        HV *xh = (HV *)SvRV(*sv);
        hv_iterinit(xh);

Archive.xs  view on Meta::CPAN

                             job.path, stage ? stage : "?",
                             strerror(errno));
            if (n > 0) {
                if ((size_t)n > sizeof errbuf) n = (int)sizeof errbuf;
                ssize_t _ignored = write(err_fd, errbuf, (size_t)n);
                (void)_ignored;
            }
        }

        parallel_job_free(&job);
    }
}

typedef struct {
    int   write_fd;
    pid_t pid;
} parallel_worker_t;

static void
do_extract_all_parallel(pTHX_ archive_handle_t *h,
                        const char *dest, size_t dest_len,
                        int parallel, int apply_xattrs,
                        SV *filter_sv, int unsafe_paths)
{
    char path_buf[ARCHIVE_PATH_MAX];
    char skip_buf[16 * 1024];
    parallel_worker_t *workers = NULL;
    int err_r = -1, err_w = -1;
    int num_started = 0;
    int has_filter;
    int dispatch_err = 0;
    char err_msg[512] = "";
    int err_pipe[2];
    struct sigaction old_sigpipe, new_sa;
    int i, rr = 0;

    if (!h || h->is_writer || h->closed)
        croak("File::Raw::Archive::extract_all: invalid handle");
    if (dest_len == 0 || dest_len >= sizeof path_buf - 2)
        croak("File::Raw::Archive::extract_all: bad dest length");
    if (parallel < 1) parallel = 1;

    has_filter = (filter_sv && SvOK(filter_sv) && SvROK(filter_sv) &&
                  SvTYPE(SvRV(filter_sv)) == SVt_PVCV);

    if (archive_mkpath(dest, 0755) < 0) {
        croak("File::Raw::Archive::extract_all: cannot create dest '%s': %s",
              dest, strerror(errno));
    }

    if (pipe(err_pipe) < 0) croak("pipe: %s", strerror(errno));
    err_r = err_pipe[0];
    err_w = err_pipe[1];

    workers = (parallel_worker_t *)calloc(parallel, sizeof *workers);
    if (!workers) {
        close(err_r); close(err_w);
        croak("File::Raw::Archive: out of memory");
    }

    /* Ignore SIGPIPE while workers are alive. A worker crash would
     * otherwise SIGPIPE the parent on the next dispatch write. */
    new_sa.sa_handler = SIG_IGN;
    sigemptyset(&new_sa.sa_mask);
    new_sa.sa_flags = 0;
    sigaction(SIGPIPE, &new_sa, &old_sigpipe);

    for (i = 0; i < parallel; i++) {
        int job_pipe[2];
        if (pipe(job_pipe) < 0) {
            int saved = errno;
            int j;
            for (j = 0; j < num_started; j++) {
                close(workers[j].write_fd);
                kill(workers[j].pid, SIGTERM);
                waitpid(workers[j].pid, NULL, 0);
            }
            close(err_r); close(err_w);
            free(workers);
            sigaction(SIGPIPE, &old_sigpipe, NULL);
            croak("File::Raw::Archive: pipe: %s", strerror(saved));
        }
        pid_t pid = fork();
        if (pid < 0) {
            int saved = errno;
            int j;
            close(job_pipe[0]); close(job_pipe[1]);
            for (j = 0; j < num_started; j++) {
                close(workers[j].write_fd);
                kill(workers[j].pid, SIGTERM);
                waitpid(workers[j].pid, NULL, 0);
            }
            close(err_r); close(err_w);
            free(workers);
            sigaction(SIGPIPE, &old_sigpipe, NULL);
            croak("File::Raw::Archive: fork: %s", strerror(saved));
        }
        if (pid == 0) {
            /* Child: keep its job-read fd and the shared err-write fd;
             * everything else inherited from the parent gets closed. */
            int j;
            close(job_pipe[1]);
            close(err_r);
            for (j = 0; j < num_started; j++) {
                close(workers[j].write_fd);
            }
            do_worker_loop(job_pipe[0], err_w);
            close(job_pipe[0]);
            close(err_w);
            _exit(0);
        }
        /* Parent. */
        close(job_pipe[0]);
        workers[num_started].write_fd = job_pipe[1];
        workers[num_started].pid = pid;
        num_started++;
    }
    close(err_w); err_w = -1;

    /* Dispatch loop. */
    for (;;) {



( run in 7.793 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )