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_ea_t xar_ea_new(xar_file_t f, const char *name) { xar_ea_t ret; ret = calloc(sizeof(struct __xar_ea_t), 1); if( !ret ) return NULL; XAR_EA(ret)->prop = xar_prop_new(f, NULL); if( !XAR_EA(ret)->prop ) { free(ret); return NULL; } xar_prop_setkey(XAR_EA(ret)->prop, "ea"); xar_prop_setvalue(XAR_EA(ret)->prop, NULL); XAR_PROP(XAR_EA(ret)->prop)->attrs = xar_attr_new(); XAR_ATTR(XAR_PROP(XAR_EA(ret)->prop)->attrs)->key = strdup("id"); asprintf((char **)&XAR_ATTR(XAR_PROP(XAR_EA(ret)->prop)->attrs)->value, "%lld", XAR_FILE(f)->nexteaid++); xar_prop_pset(f, XAR_EA(ret)->prop, "name", name); return ret; }
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_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; }
/* replace_sign: rip out all current signatures and certs and insert a new pair Since libxar is currently not capable of doing this directly, we have to create a new xar archive, copy all the files and options from the current archive, and sign the new archive */ static void replace_sign(const char *filename) { xar_t old_xar, new_xar; xar_signature_t sig; char *new_xar_path = (char *)malloc(15); strncpy(new_xar_path, "/tmp/xar.XXXXX", 15); new_xar_path = mktemp(new_xar_path); char *systemcall; int err; // open both archives old_xar = xar_open(filename, READ); if ( old_xar == NULL ) { fprintf(stderr, "Could not open archive %s!\n", filename); exit(1); } new_xar = xar_open(new_xar_path, WRITE); if( !new_xar ) { fprintf(stderr, "Error creating new archive %s\n", new_xar_path); exit(1); } // install new signature and new certs in new_xar sig = xar_signature_new(new_xar, "RSA", SigSize, &signingCallback, NULL); if ( LeafCertPath ) insert_cert(sig, LeafCertPath); if ( IntermediateCertPath ) insert_cert(sig, IntermediateCertPath); // copy options char *opts[6] = {XAR_OPT_TOCCKSUM, XAR_OPT_COMPRESSION, XAR_OPT_COALESCE, XAR_OPT_LINKSAME, XAR_OPT_RSIZE, XAR_OPT_OWNERSHIP}; int i; const char *opt; for (i=0; i<6; i++) { opt = xar_opt_get(old_xar, opts[i]); if (opt) xar_opt_set(new_xar, opts[i], opt); } // skip copy subdocs for now since we don't use them yet // copy files xar_iter_t iter = xar_iter_new(); xar_file_t f = xar_file_first(old_xar, iter); // xar_file_next iterates the archive depth-first, i.e. all children are enumerated before the siblings. const char *name; char *f_dirname, *stack_top_dirname; stack s_new = stack_new(); stack s_old = stack_new(); xar_file_t last_copied = NULL, last_added; xar_iter_t loopIter = xar_iter_new(); xar_file_t current_xar_file; for (current_xar_file = xar_file_first(old_xar, loopIter); current_xar_file; current_xar_file = xar_file_next(loopIter)) { printf("old_xar -> %s (parent: %s)\n",xar_get_path(current_xar_file),XAR_FILE(current_xar_file)->parent?xar_get_path(XAR_FILE(current_xar_file)->parent):"(nil)"); } do { // parent is the parent in the new archive! // 3 cases: // 1. the file has no parent. Happens for every file at the top level of the archive. // 2. the file's parent is the last file we added. Happens while descending down a path // 3. the file's parent is one of the ancestors of the last file (and not NULL, that would be case 1) // that means we either go back up the tree and add a sibling of one of the ancestors, or we add a // sibling on the same level xar_prop_get(f, "name", &name); // filename, without any path info if (!XAR_FILE(f)->parent) { // case 1 printf("root: %s\n",xar_get_path(f)); last_added = xar_add_from_archive(new_xar, NULL, name, old_xar, f); last_copied = f; stack_push(s_new, (void *)last_added); stack_push(s_old, (void *)last_copied); } else if (f->parent == last_copied) { // case 2 printf("child: %s -> %s\n",xar_get_path(f->parent),xar_get_path(f)); last_added = xar_add_from_archive(new_xar, last_added, name, old_xar, f); last_copied = f; stack_push(s_new, (void *)last_added); stack_push(s_old, (void *)last_copied); } else { // case 3 printf("searching for parent: %s ?\n",xar_get_path(f)); while (XAR_FILE(f)->parent != XAR_FILE(s_old->top->data)->parent) { printf("popping: %s\n",xar_get_path(XAR_FILE(s_old->top->data))); stack_pop(s_new); stack_pop(s_old); } printf("found: %s -> %s\n",xar_get_path(XAR_FILE(s_new->top->data)),xar_get_path(f)); stack_pop(s_new); stack_pop(s_old); last_added = xar_add_from_archive(new_xar, (xar_file_t)(s_new->top->data), name, old_xar, f); last_copied = f; stack_push(s_new, (void *)last_added); stack_push(s_old, (void *)last_copied); } } while (f = xar_file_next(iter)); loopIter = xar_iter_new(); for (current_xar_file = xar_file_first(new_xar, loopIter); current_xar_file; current_xar_file = xar_file_next(loopIter)) { char * current_path = xar_get_path(current_xar_file); printf("new_xar -> %s\n",current_path); } xar_iter_free(iter); stack_free(s_new); stack_free(s_old); if( xar_close(new_xar) != 0 ) { fprintf(stderr, "Error creating the archive\n"); if( !Err ) Err = 42; } xar_close(old_xar); // write signature offset to file (have to re-open so xar_close can figure out the correct offset) new_xar = xar_open(new_xar_path, READ); if( !new_xar ) { fprintf(stderr, "Error re-opening new archive %s\n", new_xar_path); unlink(new_xar_path); exit(1); } if (extract_sig_offset(new_xar, SigOffsetDumpPath)) { xar_close(new_xar); unlink(new_xar_path); exit(1); } xar_close(new_xar); // delete old archive, move new in its place unlink(filename); asprintf(&systemcall, "cp \"%s\" \"%s\"", new_xar_path, filename); err = system(systemcall); if (err) { fprintf(stderr, "Could not copy new archive to final location (system() returned %i)\n", err); unlink(new_xar_path); exit(1); } // Delete the tmp archive unlink(new_xar_path); free(systemcall); }
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; }