File-Raw-Archive
view release on metacpan or search on metacpan
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);
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 )