static xar_file_t xar_link_lookup(xar_t x, dev_t dev, ino_t ino, xar_file_t f) { char key[32]; xar_file_t ret; memset(key, 0, sizeof(key)); snprintf(key, sizeof(key)-1, "%08" DEV_HEXSTRING "%08" INO_HEXSTRING, DEV_CAST dev, INO_CAST ino); ret = xmlHashLookup(XAR(x)->ino_hash, BAD_CAST(key)); if( ret == NULL ) { xmlHashAddEntry(XAR(x)->ino_hash, BAD_CAST(key), XAR_FILE(f)); return NULL; } return ret; }
/* xar_heap_to_archive * x: archive to operate on * Returns 0 on success, -1 on error * Summary: copies the heap into the archive. */ int32_t xar_heap_to_archive(xar_t x) { long bsize; ssize_t r; int off; const char *opt; char *b; opt = xar_opt_get(x, "rsize"); if( !opt ) { bsize = 4096; } else { bsize = strtol(opt, NULL, 0); if( ((bsize == LONG_MAX) || (bsize == LONG_MIN)) && (errno == ERANGE) ) { bsize = 4096; } } b = malloc(bsize); if( !b ) return -1; lseek(XAR(x)->heap_fd, 0, SEEK_SET); while(1) { r = read(XAR(x)->heap_fd, b, bsize); if( r == 0 ) break; if( (r < 0) && (errno == EINTR) ) continue; if( r < 0 ) { free(b); return -1; } off = 0; do { r = write(XAR(x)->fd, b+off, bsize-off); if( (r < 0) && (errno != EINTR) ) { free(b); return -1; } off += r; } while( off < bsize ); } free(b); return 0; }
/* xar_check_prop * x: xar archive * name: name of property to check * Description: If XAR_OPT_PROPINCLUDE is set at all, only properties * specified for inclusion will be added. * If XAR_OPT_PROPINCLUDE is not set, and XAR_OPT_PROPEXCLUDE is set, * properies specified by XAR_OPT_PROPEXCLUDE will be omitted. * Returns: 0 for not to include, 1 for include. */ int32_t xar_check_prop(xar_t x, const char *name) { xar_attr_t i; char includeset = 0; for(i = XAR(x)->attrs; i; i = XAR_ATTR(i)->next) { if( strcmp(XAR_ATTR(i)->key, XAR_OPT_PROPINCLUDE) == 0 ) { if( strcmp(XAR_ATTR(i)->value, name) == 0 ) return 1; includeset = 1; } } if( includeset ) return 0; for(i = XAR(x)->attrs; i; i = XAR_ATTR(i)->next) { if( strcmp(XAR_ATTR(i)->key, XAR_OPT_PROPEXCLUDE) == 0 ) { if( strcmp(XAR_ATTR(i)->value, name) == 0 ) return 0; } } return 1; }
size_t xar_io_get_toc_checksum_length(xar_t x) { switch(XAR(x)->header.cksum_alg) { case XAR_CKSUM_NONE: return 0; case XAR_CKSUM_SHA1: return 20; case XAR_CKSUM_SHA256: return 32; case XAR_CKSUM_SHA512: return 64; case XAR_CKSUM_MD5: return 16; default: fprintf(stderr, "Unknown hashing algorithm, skipping\n"); return 0; }; }
int32_t xar_attrcopy_from_heap_to_stream_init(xar_t x, xar_file_t f, xar_prop_t p, xar_stream *stream) { xar_stream_state_t *state; off_t seekoff; seekoff = xar_io_get_file_offset(x, f, p); if( seekoff < 0 ) return XAR_STREAM_ERR; state = calloc(1, sizeof(xar_stream_state_t)); if( !state ) { return XAR_STREAM_ERR; } stream->state = (void*)state; state->bsize = xar_io_get_rsize(x); state->modulecount = (sizeof(xar_datamods)/sizeof(struct datamod)); state->modulecontext = calloc(1, sizeof(void*)*state->modulecount); if( !state->modulecontext ) { free(state); return XAR_STREAM_ERR; } seekoff += XAR(x)->toc_count + sizeof(xar_header_t); xar_io_seek(x, f, seekoff); stream->total_in = 0; stream->total_out = 0; state->fsize = xar_io_get_length(p); if(state->fsize == 0) { return XAR_STREAM_OK; } else if(state->fsize == -1) { free(state->modulecontext); free(state); return XAR_STREAM_ERR; } state->pending_buf = NULL; state->pending_buf_size = 0; state->x = x; state->f = f; state->p = p; return XAR_STREAM_OK; }
static void xar_io_seek(xar_t x, xar_file_t f, off_t seekoff) { int r; if( XAR(x)->fd >= 0 ) { r = lseek(XAR(x)->fd, seekoff, SEEK_SET); if( r == -1 ) { if( errno == ESPIPE ) { ssize_t rr; char *buf; unsigned int len; len = seekoff - XAR(x)->toc_count; len -= sizeof(xar_header_t); if( XAR(x)->heap_offset > len ) { xar_err_new(x); xar_err_set_file(x, f); xar_err_set_string(x, "Unable to seek"); xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); } else { len -= XAR(x)->heap_offset; buf = malloc(len); assert(buf); rr = xar_read_fd(XAR(x)->fd, buf, len); if( rr < len ) { xar_err_new(x); xar_err_set_file(x, f); xar_err_set_string(x, "Unable to seek"); xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); } XAR(x)->heap_offset += rr; free(buf); } } else { xar_err_new(x); xar_err_set_file(x, f); xar_err_set_string(x, "Unable to seek"); xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); } } } }
int32_t xar_attrcopy_to_heap(xar_t x, xar_file_t f, xar_prop_t p, read_callback rcb, void *context) { int modulecount = (sizeof(xar_datamods)/sizeof(struct datamod)); void *modulecontext[modulecount]; int r, off, i; size_t bsize, rsize; int64_t readsize=0, writesize=0, inc = 0; void *inbuf; char *tmpstr = NULL; const char *opt, *csum; off_t orig_heap_offset = XAR(x)->heap_offset; xar_file_t tmpf = NULL; xar_prop_t tmpp = NULL; memset(modulecontext, 0, sizeof(void*)*modulecount); opt = xar_opt_get(x, XAR_OPT_RSIZE); if( !opt ) { bsize = 4096; } else { bsize = strtol(opt, NULL, 0); if( ((bsize == LONG_MAX) || (bsize == LONG_MIN)) && (errno == ERANGE) ) { bsize = 4096; } } r = 1; while(r != 0) { inbuf = malloc(bsize); if( !inbuf ) return -1; r = rcb(x, f, inbuf, bsize, context); if( r < 0 ) { free(inbuf); return -1; } readsize+=r; inc += r; rsize = r; /* filter the data through the in modules */ for( i = 0; i < modulecount; i++) { if( xar_datamods[i].th_in ) { xar_datamods[i].th_in(x, f, p, &inbuf, &rsize, &(modulecontext[i])); } } /* filter the data through the out modules */ for( i = 0; i < modulecount; i++) { if( xar_datamods[i].th_out ) xar_datamods[i].th_out(x, f, p, inbuf, rsize, &(modulecontext[i])); } off = 0; if( rsize != 0 ) { do { r = write(XAR(x)->heap_fd, inbuf+off, rsize-off); if( (r < 0) && (errno != EINTR) ) return -1; off += r; writesize += r; } while( off < rsize ); } XAR(x)->heap_offset += off; free(inbuf); } /* If size is 0, don't bother having anything in the heap */ if( readsize == 0 ) { XAR(x)->heap_offset = orig_heap_offset; lseek(XAR(x)->heap_fd, -writesize, SEEK_CUR); for( i = 0; i < modulecount; i++) { if( xar_datamods[i].th_done ) xar_datamods[i].th_done(x, f, p, &(modulecontext[i])); } return 0; } /* finish up anything that still needs doing */ for( i = 0; i < modulecount; i++) { if( xar_datamods[i].th_done ) xar_datamods[i].th_done(x, f, p, &(modulecontext[i])); } XAR(x)->heap_len += writesize; tmpp = xar_prop_pget(p, "archived-checksum"); if( tmpp ) csum = xar_prop_getvalue(tmpp); tmpf = xmlHashLookup(XAR(x)->csum_hash, BAD_CAST(csum)); if( tmpf ) { const char *attr = xar_prop_getkey(p); opt = xar_opt_get(x, XAR_OPT_LINKSAME); if( opt && (strcmp(attr, "data") == 0) ) { const char *id = xar_attr_pget(tmpf, NULL, "id"); xar_prop_pset(f, NULL, "type", "hardlink"); tmpp = xar_prop_pfirst(f); if( tmpp ) tmpp = xar_prop_find(tmpp, "type"); if( tmpp ) xar_attr_pset(f, tmpp, "link", id); xar_prop_pset(tmpf, NULL, "type", "hardlink"); tmpp = xar_prop_pfirst(tmpf); if( tmpp ) tmpp = xar_prop_find(tmpp, "type"); if( tmpp ) xar_attr_pset(tmpf, tmpp, "link", "original"); tmpp = xar_prop_pfirst(f); if( tmpp ) tmpp = xar_prop_find(tmpp, "data"); xar_prop_punset(f, tmpp); XAR(x)->heap_offset = orig_heap_offset; lseek(XAR(x)->heap_fd, -writesize, SEEK_CUR); XAR(x)->heap_len -= writesize; return 0; } opt = xar_opt_get(x, XAR_OPT_COALESCE); if( opt ) { long long tmpoff; const char *offstr = NULL; tmpp = xar_prop_pfirst(tmpf); if( tmpp ) { const char *key; key = xar_prop_getkey(p); tmpp = xar_prop_find(tmpp, key); } if( tmpp ) tmpp = xar_prop_pget(tmpp, "offset"); if( tmpp ) offstr = xar_prop_getvalue(tmpp); if( offstr ) { tmpoff = strtoll(offstr, NULL, 10); XAR(x)->heap_offset = orig_heap_offset; lseek(XAR(x)->heap_fd, -writesize, SEEK_CUR); orig_heap_offset = tmpoff; XAR(x)->heap_len -= writesize; } } } else { xmlHashAddEntry(XAR(x)->csum_hash, BAD_CAST(csum), XAR_FILE(f)); } asprintf(&tmpstr, "%"PRIu64, readsize); xar_prop_pset(f, p, "size", tmpstr); free(tmpstr); asprintf(&tmpstr, "%"PRIu64, (uint64_t)orig_heap_offset); xar_prop_pset(f, p, "offset", tmpstr); free(tmpstr); tmpstr = (char *)xar_opt_get(x, XAR_OPT_COMPRESSION); if( tmpstr && (strcmp(tmpstr, XAR_OPT_VAL_NONE) == 0) ) { xar_prop_pset(f, p, "encoding", NULL); tmpp = xar_prop_pget(p, "encoding"); if( tmpp ) xar_attr_pset(f, tmpp, "style", "application/octet-stream"); } asprintf(&tmpstr, "%"PRIu64, writesize); xar_prop_pset(f, p, "length", tmpstr); free(tmpstr); return 0; }
int32_t xar_stat_extract(xar_t x, xar_file_t f, const char *file, char *buffer, size_t len) { const char *opt; int ret, fd; mode_t modet = 0; xar_prop_get(f, "type", &opt); if(opt && (strcmp(opt, "character special") == 0)) modet = S_IFCHR; if(opt && (strcmp(opt, "block special") == 0)) modet = S_IFBLK; if( modet ) { uint32_t major, minor; long long tmpll; dev_t devt; xar_prop_get(f, "device/major", &opt); tmpll = strtoll(opt, NULL, 10); if( ( (tmpll == LLONG_MIN) || (tmpll == LLONG_MAX) ) && (errno == ERANGE) ) return -1; if( (tmpll < 0) || (tmpll > 255) ) return -1; major = tmpll; xar_prop_get(f, "device/minor", &opt); tmpll = strtoll(opt, NULL, 10); if( ( (tmpll == LLONG_MIN) || (tmpll == LLONG_MAX) ) && (errno == ERANGE) ) return -1; if( (tmpll < 0) || (tmpll > 255) ) return -1; minor = tmpll; devt = xar_makedev(major, minor); unlink(file); if( mknod(file, modet, devt) ) { xar_err_new(x); xar_err_set_file(x, f); xar_err_set_string(x, "mknod: Could not create character device"); xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); return -1; } return 0; } if(opt && (strcmp(opt, "directory") == 0)) { ret = mkdir(file, 0700); if( (ret != 0) && (errno != EEXIST) ) { xar_err_new(x); xar_err_set_file(x, f); xar_err_set_string(x, "stat: Could not create directory"); xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); return ret; } return 0; } if(opt && (strcmp(opt, "symlink") == 0)) { xar_prop_get(f, "link", &opt); if( opt ) { unlink(file); ret = symlink(opt, file); if( ret != 0 ) { xar_err_new(x); xar_err_set_file(x, f); xar_err_set_string(x, "stat: Could not create symlink"); xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); } return ret; } } if(opt && (strcmp(opt, "hardlink") == 0)) { xar_file_t tmpf; opt = xar_attr_get(f, "type", "link"); if( !opt ) return 0; if( strcmp(opt, "original") == 0 ) goto CREATEFILE; tmpf = xmlHashLookup(XAR(x)->link_hash, BAD_CAST(opt)); if( !tmpf ) { xar_err_new(x); xar_err_set_file(x, f); xar_err_set_string(x, "stat: Encountered hardlink with no original"); xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); return -1; } unlink(file); if( link(XAR_FILE(tmpf)->fspath, file) != 0 ) { if( errno == ENOENT ) { xar_iter_t i; const char *ptr; i = xar_iter_new(x); for(ptr = xar_prop_first(tmpf, i); ptr; ptr = xar_prop_next(i)) { xar_iter_t a; const char *val = NULL; const char *akey, *aval; if( strncmp("data", ptr, 4) != 0 ) continue; if( xar_prop_get(tmpf, ptr, &val) ) continue; xar_prop_set(f, ptr, val); a = xar_iter_new(x); for(akey = xar_attr_first(tmpf, ptr, a); akey; akey = xar_attr_next(a)) { aval = xar_attr_get(tmpf, ptr, akey); xar_attr_set(f, ptr, akey, aval); } xar_iter_free(a); } xar_iter_free(i); xar_attr_set(f, "type", "link", "original"); return 0; } else { xar_err_new(x); xar_err_set_file(x, f); xar_err_set_string(x, "stat: Could not link hardlink to original"); xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); return -1; } } return 0; } if(opt && (strcmp(opt, "fifo") == 0)) { unlink(file); if( mkfifo(file, 0) ) { xar_err_new(x); xar_err_set_file(x, f); xar_err_set_string(x, "mkfifo: Could not create fifo"); xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); return -1; } return 0; } /* skip sockets */ if(opt && (strcmp(opt, "socket") == 0)) { return 0; } CREATEFILE: unlink(file); fd = open(file, O_RDWR|O_CREAT|O_TRUNC, 0600); if( fd > 0 ) close(fd); return 0; }
int32_t xar_attrcopy_from_heap_to_stream(xar_stream *stream) { xar_stream_state_t *state = stream->state; int r, i; size_t bsize; void *inbuf; if( state->pending_buf_size ) { return flush_stream(stream); } bsize = state->bsize; inbuf = malloc(bsize); if( !inbuf ) { return XAR_STREAM_ERR; } /* Size has been reached */ if( state->fsize == stream->total_in ) { free(inbuf); return XAR_STREAM_END; } if( (state->fsize - stream->total_in) < bsize ) bsize = state->fsize - stream->total_in; r = read(XAR(state->x)->fd, inbuf, bsize); if( r == 0 ) { free(inbuf); return XAR_STREAM_END; } if( (r < 0) && (errno == EINTR) ) { free(inbuf); return XAR_STREAM_OK; } if( r < 0 ) { free(inbuf); return XAR_STREAM_ERR; } XAR(state->x)->heap_offset += r; stream->total_in += r; bsize = r; /* filter the data through the in modules */ for( i = 0; i < state->modulecount; i++) { if( xar_datamods[i].fh_in ) { int32_t ret; ret = xar_datamods[i].fh_in(state->x, state->f, state->p, &inbuf, &bsize, &(state->modulecontext[i])); if( ret < 0 ) return XAR_STREAM_ERR; } } /* filter the data through the out modules */ for( i = 0; i < state->modulecount; i++) { if( xar_datamods[i].fh_out ) { int32_t ret; ret = xar_datamods[i].fh_out(state->x, state->f, state->p, inbuf, bsize, &(state->modulecontext[i])); if( ret < 0 ) return XAR_STREAM_ERR; } } write_to_stream(inbuf, bsize, stream); free(inbuf); return XAR_STREAM_OK; }
/* xar_attrcopy_from_heap_to_heap * This does a simple copy of the heap data from one head (read-only) to another heap (write only). * This does not set any properties or attributes of the file, so this should not be used alone. */ int32_t xar_attrcopy_from_heap_to_heap(xar_t xsource, xar_file_t fsource, xar_prop_t p, xar_t xdest, xar_file_t fdest){ int r, off; size_t bsize; int64_t fsize, inc = 0, seekoff, writesize=0; off_t orig_heap_offset = XAR(xdest)->heap_offset; void *inbuf; const char *opt; char *tmpstr = NULL; xar_prop_t tmpp; bsize = xar_io_get_rsize(xsource); seekoff = xar_io_get_file_offset(xsource, fsource, p); if( seekoff < 0 ) return -1; seekoff += XAR(xsource)->toc_count + sizeof(xar_header_t); xar_io_seek(xsource, fsource, seekoff); fsize = xar_io_get_length(p); if( fsize == 0 ) return 0; if( fsize < 0 ) return -1; inbuf = malloc(bsize); if( !inbuf ) { return -1; } while(1) { /* Size has been reached */ if( fsize == inc ) break; if( (fsize - inc) < bsize ) bsize = fsize - inc; r = read(XAR(xsource)->fd, inbuf, bsize); if( r == 0 ) break; if( (r < 0) && (errno == EINTR) ) continue; if( r < 0 ) { free(inbuf); return -1; } XAR(xsource)->heap_offset += r; inc += r; bsize = r; off = 0; do { r = write(XAR(xdest)->heap_fd, ((char *)inbuf)+off, r-off ); off += r; writesize += r; } while( off < r ); XAR(xdest)->heap_offset += off; XAR(xdest)->heap_len += off; } asprintf(&tmpstr, "%"PRIu64, (uint64_t)orig_heap_offset); opt = xar_prop_getkey(p); tmpp = xar_prop_pfirst(fdest); if( tmpp ) tmpp = xar_prop_find(tmpp, opt); if( tmpp ) xar_prop_pset(fdest, tmpp, "offset", tmpstr); free(tmpstr); free(inbuf); /* It is the caller's responsibility to copy the attributes of the file, etc, this only copies the data in the heap */ return 0; }
/* xar_copy_from_heap * This is the arcmod extraction entry point for extracting the file's * data from the heap file. * It is assumed the heap_fd is already positioned appropriately. */ int32_t xar_attrcopy_from_heap(xar_t x, xar_file_t f, xar_prop_t p, write_callback wcb, void *context) { int modulecount = (sizeof(xar_datamods)/sizeof(struct datamod)); void *modulecontext[modulecount]; int r, i; size_t bsize, def_bsize; int64_t fsize, inc = 0, seekoff; void *inbuf; memset(modulecontext, 0, sizeof(void*)*modulecount); def_bsize = xar_io_get_rsize(x); seekoff = xar_io_get_file_offset(x, f, p); if( seekoff == -1 ) { wcb(x, f, NULL, 0, context); return 0; } else if( ((seekoff == LLONG_MAX) || (seekoff == LLONG_MIN)) && (errno == ERANGE) ) { return -1; } seekoff += xar_io_get_heap_base_offset(x); xar_io_seek(x, f, seekoff); fsize = xar_io_get_length(p); if( fsize == 0 ) return 0; if( fsize < 0 ) return -1; bsize = def_bsize; inbuf = malloc(bsize); if( !inbuf ) { return -1; } while(1) { /* Size has been reached */ if( fsize == inc ) break; if( (fsize - inc) < bsize ) bsize = fsize - inc; r = read(XAR(x)->fd, inbuf, bsize); if( r == 0 ) break; if( (r < 0) && (errno == EINTR) ) continue; if( r < 0 ) { free(inbuf); return -1; } XAR(x)->heap_offset += r; inc += r; bsize = r; /* filter the data through the in modules */ for( i = 0; i < modulecount; i++) { if( xar_datamods[i].fh_in ) { int32_t ret; ret = xar_datamods[i].fh_in(x, f, p, &inbuf, &bsize, &(modulecontext[i])); if( ret < 0 ) { free(inbuf); // (Apple) don't leak inbuf return -1; } } } /* Only due the write phase, if there is a write function to call */ if(wcb){ /* filter the data through the out modules */ for( i = 0; i < modulecount; i++) { if( xar_datamods[i].fh_out ) { int32_t ret; ret = xar_datamods[i].fh_out(x, f, p, inbuf, bsize, &(modulecontext[i])); if( ret < 0 ) { free(inbuf); // (Apple) don't leak inbuf return -1; } } } wcb(x, f, inbuf, bsize, context); } bsize = def_bsize; } free(inbuf); /* finish up anything that still needs doing */ for( i = 0; i < modulecount; i++) { if( xar_datamods[i].fh_done ) { int32_t ret; ret = xar_datamods[i].fh_done(x, f, p, &(modulecontext[i])); if( ret < 0 ) return ret; } } return 0; }
void xar_err_set_file(xar_t x, xar_file_t f) { XAR(x)->errctx.file = f; return; }
int32_t xar_stat_archive(xar_t x, xar_file_t f, const char *file, const char *buffer, size_t len) { char *tmpstr; struct passwd *pw; struct group *gr; char time[128]; struct tm t; const char *type; /* no stat attributes for data from a buffer, it is just a file */ if(len){ xar_prop_set(f, "type", "file"); return 0; } if( S_ISREG(XAR(x)->sbcache.st_mode) && (XAR(x)->sbcache.st_nlink > 1) ) { xar_file_t tmpf; const char *id = xar_attr_get(f, NULL, "id"); if( !id ) { xar_err_new(x); xar_err_set_file(x, f); xar_err_set_string(x, "stat: No file id for file"); xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_CREATION); return -1; } tmpf = xar_link_lookup(x, XAR(x)->sbcache.st_dev, XAR(x)->sbcache.st_ino, f); xar_prop_set(f, "type", "hardlink"); if( tmpf ) { const char *id; id = xar_attr_get(tmpf, NULL, "id"); xar_attr_set(f, "type", "link", id); } else { xar_attr_set(f, "type", "link", "original"); } } else { type = filetype_name(XAR(x)->sbcache.st_mode & S_IFMT); xar_prop_set(f, "type", type); } /* Record major/minor device node numbers */ if( S_ISBLK(XAR(x)->sbcache.st_mode) || S_ISCHR(XAR(x)->sbcache.st_mode)) { uint32_t major, minor; char tmpstr[12]; xar_devmake(XAR(x)->sbcache.st_rdev, &major, &minor); memset(tmpstr, 0, sizeof(tmpstr)); snprintf(tmpstr, sizeof(tmpstr)-1, "%u", major); xar_prop_set(f, "device/major", tmpstr); memset(tmpstr, 0, sizeof(tmpstr)); snprintf(tmpstr, sizeof(tmpstr)-1, "%u", minor); xar_prop_set(f, "device/minor", tmpstr); } if( S_ISLNK(XAR(x)->sbcache.st_mode) ) { char link[4096]; struct stat lsb; memset(link, 0, sizeof(link)); readlink(file, link, sizeof(link)-1); xar_prop_set(f, "link", link); if( stat(file, &lsb) != 0 ) { xar_attr_set(f, "link", "type", "broken"); } else { type = filetype_name(lsb.st_mode & S_IFMT); xar_attr_set(f, "link", "type", type); } } asprintf(&tmpstr, "%04o", XAR(x)->sbcache.st_mode & (~S_IFMT)); xar_prop_set(f, "mode", tmpstr); free(tmpstr); asprintf(&tmpstr, "%"PRIu64, (uint64_t)XAR(x)->sbcache.st_uid); xar_prop_set(f, "uid", tmpstr); free(tmpstr); pw = getpwuid(XAR(x)->sbcache.st_uid); if( pw ) xar_prop_set(f, "user", pw->pw_name); asprintf(&tmpstr, "%"PRIu64, (uint64_t)XAR(x)->sbcache.st_gid); xar_prop_set(f, "gid", tmpstr); free(tmpstr); gr = getgrgid(XAR(x)->sbcache.st_gid); if( gr ) xar_prop_set(f, "group", gr->gr_name); gmtime_r(&XAR(x)->sbcache.st_atime, &t); memset(time, 0, sizeof(time)); strftime(time, sizeof(time), "%FT%T", &t); strcat(time, "Z"); xar_prop_set(f, "atime", time); gmtime_r(&XAR(x)->sbcache.st_mtime, &t); memset(time, 0, sizeof(time)); strftime(time, sizeof(time), "%FT%T", &t); strcat(time, "Z"); xar_prop_set(f, "mtime", time); gmtime_r(&XAR(x)->sbcache.st_ctime, &t); memset(time, 0, sizeof(time)); strftime(time, sizeof(time), "%FT%T", &t); strcat(time, "Z"); xar_prop_set(f, "ctime", time); flags_archive(f, &(XAR(x)->sbcache)); aacls(f, file); return 0; }
int32_t xar_err_callback(xar_t x, int32_t sev, int32_t err) { if( XAR(x)->ercallback ) return XAR(x)->ercallback(sev, err, &XAR(x)->errctx, ECTX(&XAR(x)->errctx)->usrctx); return 0; }
off_t xar_io_get_heap_base_offset(xar_t x) { return XAR(x)->toc_count + sizeof(xar_header_t); }
void xar_err_new(xar_t x) { memset(&XAR(x)->errctx, 0, sizeof(struct errctx)); XAR(x)->errctx.saved_errno = errno; return; }
void xar_err_set_errno(xar_t x, int e) { XAR(x)->errctx.saved_errno = e; return; }
void xar_err_set_string(xar_t x, const char *str) { XAR(x)->errctx.str = str; return; }
/* xar_copy_from_heap * This is the arcmod extraction entry point for extracting the file's * data from the heap file. * It is assumed the heap_fd is already positioned appropriately. */ int32_t xar_attrcopy_from_heap(xar_t x, xar_file_t f, xar_prop_t p, write_callback wcb, void *context) { int modulecount = (sizeof(xar_datamods)/sizeof(struct datamod)); void *modulecontext[modulecount]; int r, i; size_t bsize, def_bsize; int64_t fsize, inc = 0, seekoff; void *inbuf; const char *opt; xar_prop_t tmpp; memset(modulecontext, 0, sizeof(void*)*modulecount); opt = xar_opt_get(x, "rsize"); if( !opt ) { def_bsize = 4096; } else { def_bsize = strtol(opt, NULL, 0); if( ((def_bsize == LONG_MAX) || (def_bsize == LONG_MIN)) && (errno == ERANGE) ) { def_bsize = 4096; } } opt = NULL; tmpp = xar_prop_pget(p, "offset"); if( tmpp ) opt = xar_prop_getvalue(tmpp); if( !opt ) { wcb(x, f, NULL, 0, context); return 0; } else { seekoff = strtoll(opt, NULL, 0); if( ((seekoff == LLONG_MAX) || (seekoff == LLONG_MIN)) && (errno == ERANGE) ) { return -1; } } seekoff += XAR(x)->toc_count + sizeof(xar_header_t); if( XAR(x)->fd > 1 ) { r = lseek(XAR(x)->fd, seekoff, SEEK_SET); if( r == -1 ) { if( errno == ESPIPE ) { ssize_t rr; char *buf; unsigned int len; len = seekoff - XAR(x)->toc_count; len -= sizeof(xar_header_t); if( XAR(x)->heap_offset > len ) { xar_err_new(x); xar_err_set_file(x, f); xar_err_set_string(x, "Unable to seek"); xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); } else { len -= XAR(x)->heap_offset; buf = malloc(len); assert(buf); rr = read(XAR(x)->fd, buf, len); if( rr < len ) { xar_err_new(x); xar_err_set_file(x, f); xar_err_set_string(x, "Unable to seek"); xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); } free(buf); } } else { xar_err_new(x); xar_err_set_file(x, f); xar_err_set_string(x, "Unable to seek"); xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); } } } opt = NULL; tmpp = xar_prop_pget(p, "length"); if( tmpp ) opt = xar_prop_getvalue(tmpp); if( !opt ) { return 0; } else { fsize = strtoll(opt, NULL, 10); if( ((fsize == LLONG_MAX) || (fsize == LLONG_MIN)) && (errno == ERANGE) ) { return -1; } } bsize = def_bsize; inbuf = malloc(bsize); if( !inbuf ) { return -1; } while(1) { /* Size has been reached */ if( fsize == inc ) break; if( (fsize - inc) < bsize ) bsize = fsize - inc; r = read(XAR(x)->fd, inbuf, bsize); if( r == 0 ) break; if( (r < 0) && (errno == EINTR) ) continue; if( r < 0 ) { free(inbuf); return -1; } XAR(x)->heap_offset += r; inc += r; bsize = r; /* filter the data through the in modules */ for( i = 0; i < modulecount; i++) { if( xar_datamods[i].fh_in ) { int32_t ret; ret = xar_datamods[i].fh_in(x, f, p, &inbuf, &bsize, &(modulecontext[i])); if( ret < 0 ) return -1; } } /* Only due the write phase, if there is a write function to call */ if(wcb){ /* filter the data through the out modules */ for( i = 0; i < modulecount; i++) { if( xar_datamods[i].fh_out ) { int32_t ret; ret = xar_datamods[i].fh_out(x, f, p, inbuf, bsize, &(modulecontext[i])); if( ret < 0 ) return -1; } } wcb(x, f, inbuf, bsize, context); } free(inbuf); bsize = def_bsize; inbuf = malloc(bsize); } free(inbuf); /* finish up anything that still needs doing */ for( i = 0; i < modulecount; i++) { if( xar_datamods[i].fh_done ) { int32_t ret; ret = xar_datamods[i].fh_done(x, f, p, &(modulecontext[i])); if( ret < 0 ) return ret; } } return 0; }
int32_t xar_attrcopy_to_heap(xar_t x, xar_file_t f, xar_prop_t p, read_callback rcb, void *context) { int modulecount = (sizeof(xar_datamods)/sizeof(struct datamod)); void *modulecontext[modulecount]; int r, i; size_t bsize, rsize; int64_t readsize=0, writesize=0, inc = 0, this_write=0; void *inbuf; char *tmpstr = NULL; const char *opt = NULL, *csum = NULL; off_t orig_heap_offset = XAR(x)->heap_offset; xar_file_t tmpf = NULL; xar_prop_t tmpp = NULL; memset(modulecontext, 0, sizeof(void*)*modulecount); bsize = xar_io_get_rsize(x); r = 1; // (Apple) allocate once inbuf = malloc(bsize); if( !inbuf ) return -1; while(r != 0) { r = rcb(x, f, inbuf, bsize, context); if( r < 0 ) { free(inbuf); return -1; } readsize+=r; inc += r; rsize = r; /* filter the data through the in modules */ for( i = 0; i < modulecount; i++) { if( xar_datamods[i].th_in ) { xar_datamods[i].th_in(x, f, p, &inbuf, &rsize, &(modulecontext[i])); } } /* filter the data through the out modules */ for( i = 0; i < modulecount; i++) { if( xar_datamods[i].th_out ) xar_datamods[i].th_out(x, f, p, inbuf, rsize, &(modulecontext[i])); } size_t written = 0; if( rsize != 0 ) { while(written < rsize) { this_write = xar_write_fd(XAR(x)->heap_fd, inbuf, rsize); if( this_write < 0 ) { xar_err_new(x); xar_err_set_string(x, "write(2) error when writing to heap"); xar_err_set_errno(x, errno); xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_CREATION); free(inbuf); return -1; } written += this_write; } } XAR(x)->heap_offset += written; writesize += written; } free(inbuf); /* If size is 0, don't bother having anything in the heap */ if( readsize == 0 ) { XAR(x)->heap_offset = orig_heap_offset; lseek(XAR(x)->heap_fd, -writesize, SEEK_CUR); for( i = 0; i < modulecount; i++) { if( xar_datamods[i].th_done ) xar_datamods[i].th_done(x, f, p, &(modulecontext[i])); } return 0; } /* finish up anything that still needs doing */ for( i = 0; i < modulecount; i++) { if( xar_datamods[i].th_done ) xar_datamods[i].th_done(x, f, p, &(modulecontext[i])); } XAR(x)->heap_len += writesize; tmpp = xar_prop_pget(p, "archived-checksum"); if( tmpp ) csum = xar_prop_getvalue(tmpp); if( csum ) tmpf = xmlHashLookup(XAR(x)->csum_hash, BAD_CAST(csum)); if( tmpf ) { const char *attr = xar_prop_getkey(p); opt = xar_opt_get(x, XAR_OPT_LINKSAME); if( opt && (strcmp(attr, "data") == 0) ) { const char *id = xar_attr_pget(tmpf, NULL, "id"); xar_prop_pset(f, NULL, "type", "hardlink"); tmpp = xar_prop_pfirst(f); if( tmpp ) tmpp = xar_prop_find(tmpp, "type"); if( tmpp ) xar_attr_pset(f, tmpp, "link", id); xar_prop_pset(tmpf, NULL, "type", "hardlink"); tmpp = xar_prop_pfirst(tmpf); if( tmpp ) tmpp = xar_prop_find(tmpp, "type"); if( tmpp ) xar_attr_pset(tmpf, tmpp, "link", "original"); tmpp = xar_prop_pfirst(f); if( tmpp ) tmpp = xar_prop_find(tmpp, "data"); xar_prop_punset(f, tmpp); XAR(x)->heap_offset = orig_heap_offset; lseek(XAR(x)->heap_fd, -writesize, SEEK_CUR); XAR(x)->heap_len -= writesize; return 0; } opt = xar_opt_get(x, XAR_OPT_COALESCE); if( opt ) { long long tmpoff; const char *offstr = NULL; tmpp = xar_prop_pfirst(tmpf); if( tmpp ) { const char *key; key = xar_prop_getkey(p); tmpp = xar_prop_find(tmpp, key); } if( tmpp ) tmpp = xar_prop_pget(tmpp, "offset"); if( tmpp ) offstr = xar_prop_getvalue(tmpp); if( offstr ) { tmpoff = strtoll(offstr, NULL, 10); XAR(x)->heap_offset = orig_heap_offset; lseek(XAR(x)->heap_fd, -writesize, SEEK_CUR); orig_heap_offset = tmpoff; XAR(x)->heap_len -= writesize; } } } else if( csum ) { xmlHashAddEntry(XAR(x)->csum_hash, BAD_CAST(csum), XAR_FILE(f)); } else { xar_err_new(x); xar_err_set_file(x, f); xar_err_set_string(x, "No archived-checksum"); xar_err_callback(x, XAR_SEVERITY_WARNING, XAR_ERR_ARCHIVE_CREATION); } asprintf(&tmpstr, "%"PRIu64, readsize); xar_prop_pset(f, p, "size", tmpstr); free(tmpstr); asprintf(&tmpstr, "%"PRIu64, (uint64_t)orig_heap_offset); xar_prop_pset(f, p, "offset", tmpstr); free(tmpstr); tmpstr = (char *)xar_opt_get(x, XAR_OPT_COMPRESSION); if( tmpstr && (strcmp(tmpstr, XAR_OPT_VAL_NONE) == 0) ) { xar_prop_pset(f, p, "encoding", NULL); tmpp = xar_prop_pget(p, "encoding"); if( tmpp ) xar_attr_pset(f, tmpp, "style", "application/octet-stream"); } asprintf(&tmpstr, "%"PRIu64, writesize); xar_prop_pset(f, p, "length", tmpstr); free(tmpstr); return 0; }
/* xar_attrcopy_from_heap_to_heap * This does a simple copy of the heap data from one head (read-only) to another heap (write only). * This does not set any properties or attributes of the file, so this should not be used alone. */ int32_t xar_attrcopy_from_heap_to_heap(xar_t xsource, xar_file_t fsource, xar_prop_t p, xar_t xdest, xar_file_t fdest){ int r, off; size_t bsize; int64_t fsize, inc = 0, seekoff, writesize=0; off_t orig_heap_offset = XAR(xdest)->heap_offset; void *inbuf; const char *opt; char *tmpstr = NULL; xar_prop_t tmpp; opt = xar_opt_get(xsource, "rsize"); if( !opt ) { bsize = 4096; } else { bsize = strtol(opt, NULL, 0); if( ((bsize == LONG_MAX) || (bsize == LONG_MIN)) && (errno == ERANGE) ) { bsize = 4096; } } tmpp = xar_prop_pget(p, "offset"); if( tmpp ) opt = xar_prop_getvalue(tmpp); seekoff = strtoll(opt, NULL, 0); if( ((seekoff == LLONG_MAX) || (seekoff == LLONG_MIN)) && (errno == ERANGE) ) { return -1; } seekoff += XAR(xsource)->toc_count + sizeof(xar_header_t); if( XAR(xsource)->fd > 1 ) { r = lseek(XAR(xsource)->fd, seekoff, SEEK_SET); if( r == -1 ) { if( errno == ESPIPE ) { ssize_t rr; char *buf; unsigned int len; len = seekoff - XAR(xsource)->toc_count; len -= sizeof(xar_header_t); if( XAR(xsource)->heap_offset > len ) { xar_err_new(xsource); xar_err_set_file(xsource, fsource); xar_err_set_string(xsource, "Unable to seek"); xar_err_callback(xsource, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); } else { len -= XAR(xsource)->heap_offset; buf = malloc(len); assert(buf); rr = read(XAR(xsource)->fd, buf, len); if( rr < len ) { xar_err_new(xsource); xar_err_set_file(xsource, fsource); xar_err_set_string(xsource, "Unable to seek"); xar_err_callback(xsource, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); } free(buf); } } else { xar_err_new(xsource); xar_err_set_file(xsource, fsource); xar_err_set_string(xsource, "Unable to seek"); xar_err_callback(xsource, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION); } } } opt = NULL; tmpp = xar_prop_pget(p, "length"); if( tmpp ) opt = xar_prop_getvalue(tmpp); if( !opt ) { return 0; } else { fsize = strtoll(opt, NULL, 10); if( ((fsize == LLONG_MAX) || (fsize == LLONG_MIN)) && (errno == ERANGE) ) { return -1; } } inbuf = malloc(bsize); if( !inbuf ) { return -1; } while(1) { /* Size has been reached */ if( fsize == inc ) break; if( (fsize - inc) < bsize ) bsize = fsize - inc; r = read(XAR(xsource)->fd, inbuf, bsize); if( r == 0 ) break; if( (r < 0) && (errno == EINTR) ) continue; if( r < 0 ) { free(inbuf); return -1; } XAR(xsource)->heap_offset += r; inc += r; bsize = r; off = 0; do { r = write(XAR(xdest)->heap_fd, inbuf+off, r-off ); off += r; writesize += r; } while( off < r ); XAR(xdest)->heap_offset += off; XAR(xdest)->heap_len += off; } asprintf(&tmpstr, "%"PRIu64, (uint64_t)orig_heap_offset); opt = xar_prop_getkey(p); tmpp = xar_prop_pfirst(fdest); if( tmpp ) tmpp = xar_prop_find(tmpp, opt); if( tmpp ) xar_prop_pset(fdest, tmpp, "offset", tmpstr); free(tmpstr); free(inbuf); /* It is the caller's responsibility to copy the attributes of the file, etc, this only copies the data in the heap */ return 0; }
void xar_register_errhandler(xar_t x, err_handler callback, void *usrctx) { ECTX(&XAR(x)->errctx)->x = x; ECTX(&XAR(x)->errctx)->usrctx = usrctx; XAR(x)->ercallback = callback; return; }