struct slist *build_manifest_with_data_files(const char *path, const char *datapath, int entries, int data_files) { struct blk *b=NULL; struct slist *slist=NULL; struct manio *manio=NULL; struct fzp *fzp=NULL; char spath[256]=""; char cpath[256]=""; fail_unless((manio=manio_open_phase3(path, "wb", PROTO_2, RMANIFEST_RELATIVE))!=NULL); slist=do_build_manifest(manio, PROTO_2, entries, data_files); fail_unless(!manio_close(&manio)); for(b=slist->blist->head; b; b=b->next) { snprintf(spath, sizeof(spath), "%s/%s", datapath, uint64_to_savepathstr(b->savepath)); if(strcmp(spath, cpath)) { snprintf(cpath, sizeof(cpath), "%s", spath); fzp_close(&fzp); } if(!fzp) { fail_unless(!build_path_w(cpath)); fail_unless((fzp=fzp_open(cpath, "wb"))!=NULL); } fzp_printf(fzp, "%c%04X%s", CMD_DATA, strlen("data"), "data"); } fzp_close(&fzp); return slist; }
static void setup_datadir_tmp_some_files_done_already( struct slist *slist, struct fdirs *fdirs, struct conf **confs) { int done=5; struct sbuf *s; char *finpath; char *tmppath; struct stat statp; memset(&statp, 0, sizeof(struct stat)); setup_datadir_tmp(slist, fdirs, confs); for(s=slist->head; s; s=s->next) { if(!sbuf_is_filedata(s)) continue; tmppath=datadirtmp_path(fdirs, s); finpath=datadir_path(fdirs, s); build_path_w(finpath); fail_unless(!do_link(tmppath, finpath, &statp, confs, /*overwrite*/0)); // Unlink some of the original paths, and not others. if(done>3) unlink(tmppath); if(done--==0) break; } }
static int manio_open_next_fpath(struct manio *manio) { static struct stat statp; free_w(&manio->offset->fpath); if(!(manio->offset->fpath=get_next_fpath(manio, manio->offset))) return -1; if(!strcmp(manio->mode, MANIO_MODE_READ) && lstat(manio->offset->fpath, &statp)) return 0; if(build_path_w(manio->offset->fpath)) return -1; switch(manio->phase) { case 2: if(!(manio->fzp=fzp_open(manio->offset->fpath, manio->mode))) return -1; return 0; case 1: case 3: default: if(!(manio->fzp=fzp_gzopen(manio->offset->fpath, manio->mode))) return -1; return 0; } }
static FILE *file_open_w(const char *path, const char *mode) { FILE *fp; if(build_path_w(path)) return NULL; fp=open_file(path, "wb"); return fp; }
static struct asfd *asfd_setup(const char *outputpath) { struct asfd *asfd; fail_unless((asfd=asfd_alloc())!=NULL); fail_unless((asfd->rbuf=iobuf_alloc())!=NULL); asfd->write=my_asfd_write; fail_unless(!build_path_w(outputpath)); fail_unless((output=fzp_open(outputpath, "wb"))!=NULL); json_set_pretty_print(1); return asfd; }
int manio_open_next_fpath(struct manio *manio) { static struct stat statp; if(!(manio->offset.fpath=get_next_fpath(manio))) return -1; if(!strcmp(manio->mode, MANIO_MODE_READ) && lstat(manio->offset.fpath, &statp)) return 0; if(build_path_w(manio->offset.fpath) || !(manio->fzp=fzp_gzopen(manio->offset.fpath, manio->mode))) return -1; return 0; }
static int open_next_fpath(struct manio *manio) { static struct stat statp; if(manio->lpath) free(manio->lpath); manio->lpath=manio->fpath; if(!(manio->fpath=get_next_fpath(manio))) return -1; if(!strcmp(manio->mode, MANIO_MODE_READ) && lstat(manio->fpath, &statp)) return 0; if(build_path_w(manio->fpath) || !(manio->zp=gzopen_file(manio->fpath, manio->mode))) return -1; return 0; }
static void do_build_storage_dirs(struct sdirs *sdirs, struct sd *s, int len, int compressed_logs) { int i=0; time_t t=0; char backup[128]=""; char timestamp_path[128]=""; fail_unless(!build_path_w(sdirs->client)); fail_unless(!mkdir(sdirs->client, 0777)); for(i=0; i<len; i++) { snprintf(backup, sizeof(backup), "%s/%s", sdirs->client, s[i].timestamp); snprintf(timestamp_path, sizeof(timestamp_path), "%s/timestamp", backup); fail_unless(!mkdir(backup, 0777)); fail_unless(!timestamp_write(timestamp_path, s[i].timestamp)); if(s[i].flags & BU_CURRENT) fail_unless(!symlink(s[i].timestamp, sdirs->current)); if(s[i].flags & BU_WORKING) fail_unless(!symlink(s[i].timestamp, sdirs->working)); if(s[i].flags & BU_FINISHING) fail_unless(!symlink(s[i].timestamp, sdirs->finishing)); if(s[i].flags & BU_MANIFEST) create_file(backup, "manifest", compressed_logs); if(s[i].flags & BU_LOG_BACKUP) create_file(backup, "log", compressed_logs); if(s[i].flags & BU_LOG_RESTORE) create_file(backup, "restorelog", compressed_logs); if(s[i].flags & BU_LOG_VERIFY) create_file(backup, "verifylog", compressed_logs); if(sdirs->protocol==PROTO_1) { if(s[i].flags & BU_HARDLINKED) create_file(backup, "hardlinked", 0); } // This one is never compressed. if(sdirs->global_sparse) build_file(sdirs->global_sparse, NULL); t+=60*60*24; // Add one day. } }
static int sort_and_write_dindex(struct manio *manio) { int i; int ret=-1; struct fzp *fzp=NULL; char msg[32]=""; char *path=NULL; struct iobuf wbuf; struct blk blk; int dindex_count=manio->dindex_count; uint64_t *dindex_sort=manio->dindex_sort; if(!dindex_sort) return 0; snprintf(msg, sizeof(msg), "%08"PRIX64, manio->offset->fcount-1); if(!(path=prepend_s(manio->dindex_dir, msg)) || build_path_w(path) || !(fzp=fzp_gzopen(path, MANIO_MODE_WRITE))) goto end; qsort(dindex_sort, dindex_count, sizeof(uint64_t), uint64_t_sort); for(i=0; i<dindex_count; i++) { // Do not bother with duplicates. if(i && dindex_sort[i]==dindex_sort[i-1]) continue; blk.savepath=dindex_sort[i]; blk_to_iobuf_savepath(&blk, &wbuf); if(iobuf_send_msg_fzp(&wbuf, fzp)) return -1; } if(fzp_close(&fzp)) { logp("Error closing %s in %s: %s\n", path, __func__, strerror(errno)); goto end; } manio->dindex_count=0; ret=0; end: fzp_close(&fzp); free_w(&path); return ret; }
static int sort_and_write_hooks(struct manio *manio) { int i; int ret=-1; struct fzp *fzp=NULL; char msg[32]=""; char *path=NULL; int hook_count=manio->hook_count; uint64_t *hook_sort=manio->hook_sort; if(!hook_sort) return 0; snprintf(msg, sizeof(msg), "%08"PRIX64, manio->offset->fcount-1); if(!(path=prepend_s(manio->hook_dir, msg)) || build_path_w(path) || !(fzp=fzp_gzopen(path, MANIO_MODE_WRITE))) goto end; qsort(hook_sort, hook_count, sizeof(uint64_t), uint64_t_sort); if(write_hook_header(fzp, manio->rmanifest, msg)) goto end; for(i=0; i<hook_count; i++) { // Do not bother with duplicates. if(i && hook_sort[i]==hook_sort[i-1]) continue; if(to_fzp_fingerprint(fzp, hook_sort[i])) goto end; } if(fzp_close(&fzp)) { logp("Error closing %s in %s: %s\n", path, __func__, strerror(errno)); goto end; } if(manio_write_fcount(manio)) goto end; manio->hook_count=0; ret=0; end: fzp_close(&fzp); free_w(&path); return ret; }
static int get_data_lock(struct lock *lock, struct dpth *dpth, const char *path) { int ret=-1; char *p=NULL; char *lockfile=NULL; // Use just the first three components, excluding sig number. if(!(p=prepend_slash(dpth->base_path, path, 14)) || !(lockfile=prepend(p, ".lock", strlen(".lock"), ""))) goto end; if(lock_init(lock, lockfile) || build_path_w(lock->path)) goto end; lock_get_quick(lock); ret=0; end: if(p) free(p); if(lockfile) free(lockfile); return ret; }
void build_sparse_index(const char *path, int manifests, int fingerprints) { struct fzp *fzp; fail_unless(!build_path_w(path)); fail_unless((fzp=fzp_gzopen(path, "wb"))!=NULL); for(int m=0; m<manifests; m++) { char mpath[256]; snprintf(mpath, sizeof(mpath), "some/manifest/%d", m); fzp_printf(fzp, "%c%04lX%s\n", CMD_MANIFEST, strlen(mpath), mpath); for(int f=0; f<fingerprints; f++) fail_unless(!to_fzp_fingerprint(fzp, prng_next64())); } fail_unless(!fzp_close(&fzp)); }
static int sort_and_write_hooks(struct manio *manio) { int i; int ret=-1; struct fzp *fzp=NULL; char comp[32]=""; char *path=NULL; int hook_count=manio->hook_count; char **hook_sort=manio->hook_sort; if(!hook_sort) return 0; snprintf(comp, sizeof(comp), "%08"PRIX64, manio->offset.fcount-1); if(!(path=prepend_s(manio->hook_dir, comp)) || build_path_w(path) || !(fzp=fzp_gzopen(path, manio->mode))) goto end; qsort(hook_sort, hook_count, sizeof(char *), strsort); if(write_hook_header(manio, fzp, comp)) goto end; for(i=0; i<hook_count; i++) { // Do not bother with duplicates. if(i && !strcmp(hook_sort[i], hook_sort[i-1])) continue; fzp_printf(fzp, "%c%04X%s\n", CMD_FINGERPRINT, (unsigned int)strlen(hook_sort[i]), hook_sort[i]); } if(fzp_close(&fzp)) { logp("Error closing %s in %s: %s\n", path, __func__, strerror(errno)); goto end; } if(manio_write_fcount(manio)) goto end; manio->hook_count=0; ret=0; end: fzp_close(&fzp); free_w(&path); return ret; }
static int do_rename_w(const char *a, const char *b, const char *cname, struct bu *bu) { int ret=-1; char *target=NULL; char new_name[256]=""; snprintf(new_name, sizeof(new_name), "%s-%s", cname, bu->basename); if(!(target=prepend_s(b, new_name)) || build_path_w(target)) goto end; if(do_rename(a, target)) { logp("Error when trying to rename for delete %s\n", a); goto end; } ret=0; end: free_w(&target); return ret; }
static int sort_and_write_hooks(struct manio *manio) { int i; int ret=-1; gzFile zp=NULL; char comp[32]=""; char *path=NULL; int hook_count=manio->hook_count; char **hook_sort=manio->hook_sort; if(!hook_sort) return 0; snprintf(comp, sizeof(comp), "%08lX", manio->fcount-1); if(!(path=prepend_s(manio->hook_dir, comp)) || build_path_w(path) || !(zp=gzopen_file(path, manio->mode))) goto end; qsort(hook_sort, hook_count, sizeof(char *), strsort); if(write_hook_header(manio, zp, comp)) goto end; for(i=0; i<hook_count; i++) { // Do not bother with duplicates. if(i && !strcmp(hook_sort[i], hook_sort[i-1])) continue; gzprintf(zp, "%c%04X%s\n", CMD_FINGERPRINT, (unsigned int)strlen(hook_sort[i]), hook_sort[i]); } if(gzclose_fp(&zp)) { logp("Error closing %s in %s: %s\n", path, __func__, strerror(errno)); goto end; } manio->hook_count=0; ret=0; end: gzclose_fp(&zp); if(path) free(path); return ret; }
END_TEST START_TEST(test_protocol1_verify_file_gzip_read_failure) { struct asfd *asfd; struct cntr *cntr; struct sbuf *sb; const char *path="somepath"; const char *datapth="/datapth"; const char *endfile="0:0"; const char *best=BASE "/existent"; const char *plain_text="some plain text"; size_t s; struct fzp *fzp; s=strlen(plain_text); clean(); cntr=setup_cntr(); sb=setup_sbuf(path, datapth, endfile, 1/*compression*/); // Make a corrupt gzipped file. build_path_w(best); fail_unless((fzp=fzp_gzopen(best, "wb"))!=NULL); fail_unless(fzp_write(fzp, plain_text, s)==s); fail_unless(!fzp_close(&fzp)); fail_unless((fzp=fzp_open(best, "r+b"))!=NULL); fail_unless(!fzp_seek(fzp, 10, SEEK_SET)); fail_unless(fzp_write(fzp, "aa", 2)==2); fail_unless(!fzp_close(&fzp)); asfd=asfd_mock_setup(&areads, &awrites); setup_error_while_reading(asfd, best); // Returns 0 so that the parent process continues. fail_unless(!verify_file(asfd, sb, 0 /*patches*/, best, cntr)); fail_unless(cntr->ent[CMD_WARNING]->count==1); tear_down(&sb, &cntr, NULL, &asfd); }
static enum asl_ret restore_spool_func(struct asfd *asfd, struct conf **confs, void *param) { static char **datpath; static struct iobuf *rbuf; datpath=(char **)param; rbuf=asfd->rbuf; if(!strncmp_w(rbuf->buf, "dat=")) { char *fpath=NULL; if(!(fpath=prepend_s(*datpath, rbuf->buf+4)) || build_path_w(fpath) || receive_a_file(asfd, fpath, get_cntr(confs))) return ASL_END_ERROR; iobuf_free_content(rbuf); } else if(!strcmp(rbuf->buf, "datfilesend")) { if(asfd->write_str(asfd, CMD_GEN, "datfilesend_ok")) return ASL_END_ERROR; return ASL_END_OK; } return ASL_CONTINUE; }
int backup_phase2_server_protocol1(struct async *as, struct sdirs *sdirs, const char *incexc, int resume, struct conf **cconfs) { int ret=0; struct manio *p1manio=NULL; struct dpth *dpth=NULL; char *deltmppath=NULL; char *last_requested=NULL; // Where to write changed data. // Data is not getting written to a compressed file. // This is important for recovery if the power goes. struct fzp *chfp=NULL; struct fzp *ucfp=NULL; // unchanged data struct fzp *cmanfp=NULL; // previous (current) manifest. struct sbuf *cb=NULL; // file list in current manifest struct sbuf *p1b=NULL; // file list from client struct sbuf *rb=NULL; // receiving file from client struct asfd *asfd=as->asfd; int breaking=0; int breakcount=0; if(get_int(cconfs[OPT_BREAKPOINT])>=2000 && get_int(cconfs[OPT_BREAKPOINT])<3000) { breaking=get_int(cconfs[OPT_BREAKPOINT]); breakcount=breaking-2000; } logp("Begin phase2 (receive file data)\n"); if(!(dpth=dpth_alloc()) || dpth_protocol1_init(dpth, sdirs->currentdata, get_int(cconfs[OPT_MAX_STORAGE_SUBDIRS]))) goto error; if(open_previous_manifest(&cmanfp, sdirs, incexc, cconfs)) goto error; if(get_int(cconfs[OPT_DIRECTORY_TREE])) { // Need to make sure we do not try to create a path that is // too long. if(build_path_w(sdirs->treepath)) goto error; treepathlen=strlen(sdirs->treepath); init_fs_max(sdirs->treepath); } if(!(p1manio=manio_alloc()) || manio_init_read(p1manio, sdirs->phase1data) || !(cb=sbuf_alloc(cconfs)) || !(p1b=sbuf_alloc(cconfs)) || !(rb=sbuf_alloc(cconfs))) goto error; manio_set_protocol(p1manio, PROTO_1); if(resume && do_resume(p1manio, sdirs, dpth, cconfs)) goto error; // Unchanged and changed should now be truncated correctly, we just // have to open them for appending. if(!(ucfp=fzp_open(sdirs->unchanged, "a+b")) || !(chfp=fzp_open(sdirs->changed, "a+b"))) goto error; if(manio_closed(p1manio) && manio_open_next_fpath(p1manio)) goto error; while(1) { if(breaking) { if(breakcount--==0) return breakpoint(cconfs, __func__); } //printf("in loop, %s %s %c\n", // cmanfp?"got cmanfp":"no cmanfp", // rb->path.buf?:"no rb->path", // rb->path.buf?'X':rb->path.cmd); if(write_status(CNTR_STATUS_BACKUP, rb->path.buf?rb->path.buf:p1b->path.buf, cconfs)) goto error; if(last_requested || manio_closed(p1manio) || asfd->writebuflen) { switch(do_stuff_to_receive(asfd, sdirs, cconfs, rb, chfp, dpth, &last_requested)) { case 0: break; case 1: goto end; // Finished ok. case -1: goto error; } } switch(do_stuff_to_send(asfd, p1b, &last_requested)) { case 0: break; case 1: continue; case -1: goto error; } if(manio_closed(p1manio)) continue; sbuf_free_content(p1b); switch(manio_sbuf_fill_phase1(p1manio, asfd, p1b, NULL, sdirs, cconfs)) { case 0: break; case 1: manio_close(p1manio); if(asfd->write_str(asfd, CMD_GEN, "backupphase2end")) goto error; break; case -1: goto error; } if(!cmanfp) { // No old manifest, need to ask for a new file. if(process_new(sdirs, cconfs, p1b, ucfp)) goto error; continue; } // Have an old manifest, look for it there. // Might already have it, or be ahead in the old // manifest. if(cb->path.buf) switch(maybe_process_file(asfd, sdirs, cb, p1b, ucfp, cconfs)) { case 0: break; case 1: continue; case -1: goto error; } while(cmanfp) { sbuf_free_content(cb); switch(sbufl_fill(cb, asfd, cmanfp, cconfs)) { case 0: break; case 1: fzp_close(&cmanfp); if(process_new(sdirs, cconfs, p1b, ucfp)) goto error; continue; case -1: goto error; } switch(maybe_process_file(asfd, sdirs, cb, p1b, ucfp, cconfs)) { case 0: continue; case 1: break; case -1: goto error; } break; } } error: ret=-1; end: if(fzp_close(&chfp)) { logp("error closing %s in %s\n", sdirs->changed, __func__); ret=-1; } if(fzp_close(&ucfp)) { logp("error closing %s in %s\n", sdirs->unchanged, __func__); ret=-1; } free_w(&deltmppath); sbuf_free(&cb); sbuf_free(&p1b); sbuf_free(&rb); manio_free(&p1manio); fzp_close(&cmanfp); dpth_free(&dpth); if(!ret) unlink(sdirs->phase1data); logp("End phase2 (receive file data)\n"); return ret; }
int champ_chooser_server(struct sdirs *sdirs, struct conf **confs, int resume) { int s; int ret=-1; int len; struct asfd *asfd=NULL; struct sockaddr_un local; struct lock *lock=NULL; struct async *as=NULL; int started=0; struct scores *scores=NULL; const char *directory=get_string(confs[OPT_DIRECTORY]); if(!(lock=lock_alloc_and_init(sdirs->champlock)) || build_path_w(sdirs->champlock)) goto end; lock_get(lock); switch(lock->status) { case GET_LOCK_GOT: log_fzp_set(sdirs->champlog, confs); logp("Got champ lock for dedup_group: %s\n", get_string(confs[OPT_DEDUP_GROUP])); break; case GET_LOCK_NOT_GOT: case GET_LOCK_ERROR: default: //logp("Did not get champ lock\n"); goto end; } if((s=socket(AF_UNIX, SOCK_STREAM, 0))<0) { logp("socket error in %s: %s\n", __func__, strerror(errno)); goto end; } memset(&local, 0, sizeof(struct sockaddr_un)); local.sun_family=AF_UNIX; snprintf(local.sun_path, sizeof(local.sun_path), "%s", sdirs->champsock); len=strlen(local.sun_path)+sizeof(local.sun_family)+1; unlink(sdirs->champsock); if(bind(s, (struct sockaddr *)&local, len)<0) { logp("bind error in %s: %s\n", __func__, strerror(errno)); goto end; } if(listen(s, 5)<0) { logp("listen error in %s: %s\n", __func__, strerror(errno)); goto end; } if(!(as=async_alloc()) || as->init(as, 0) || !(asfd=setup_asfd(as, "champ chooser main socket", &s, /*listen*/""))) goto end; asfd->fdtype=ASFD_FD_SERVER_LISTEN_MAIN; // I think that this is probably the best point at which to run a // cleanup job to delete unused data files, because no other process // can fiddle with the dedup_group at this point. // Cannot do it on a resume, or it will delete files that are // referenced in the backup we are resuming. if(delete_unused_data_files(sdirs, resume)) goto end; // Load the sparse indexes for this dedup group. if(!(scores=champ_chooser_init(sdirs->data))) goto end; while(1) { for(asfd=as->asfd->next; asfd; asfd=asfd->next) { if(!asfd->blist->head || asfd->blist->head->got==BLK_INCOMING) continue; if(results_to_fd(asfd)) goto end; } int removed; switch(as->read_write(as)) { case 0: // Check the main socket last, as it might add // a new client to the list. for(asfd=as->asfd->next; asfd; asfd=asfd->next) { while(asfd->rbuf->buf) { if(deal_with_client_rbuf(asfd, directory, scores)) goto end; // Get as much out of the // readbuf as possible. if(asfd->parse_readbuf(asfd)) goto end; } } if(as->asfd->new_client) { // Incoming client. as->asfd->new_client=0; if(champ_chooser_new_client(as, confs)) goto end; started=1; } break; default: removed=0; // Maybe one of the fds had a problem. // Find and remove it and carry on if possible. for(asfd=as->asfd->next; asfd; ) { struct asfd *a; if(!asfd->want_to_remove) { asfd=asfd->next; continue; } as->asfd_remove(as, asfd); logp("%s: disconnected fd %d\n", asfd->desc, asfd->fd); a=asfd->next; asfd_free(&asfd); asfd=a; removed++; } if(removed) break; // If we got here, there was no fd to remove. // It is a fatal error. goto end; } if(started && !as->asfd->next) { logp("All clients disconnected.\n"); ret=0; break; } } end: logp("champ chooser exiting: %d\n", ret); champ_chooser_free(&scores); log_fzp_set(NULL, confs); async_free(&as); asfd_free(&asfd); // This closes s for us. close_fd(&s); unlink(sdirs->champsock); // FIX THIS: free asfds. lock_release(lock); lock_free(&lock); return ret; }
/* Merge two files of sorted dindexes into each other. */ int merge_dindexes(const char *dst, const char *srca, const char *srcb) { int ret=-1; struct sbuf *asb=NULL; struct sbuf *bsb=NULL; struct fzp *azp=NULL; struct fzp *bzp=NULL; struct fzp *dzp=NULL; uint64_t *anew=NULL; uint64_t *bnew=NULL; if(!(asb=sbuf_alloc(PROTO_2)) || (srcb && !(bsb=sbuf_alloc(PROTO_2)))) goto end; if(build_path_w(dst)) goto end; if((srca && !(azp=fzp_gzopen(srca, "rb"))) || (srcb && !(bzp=fzp_gzopen(srcb, "rb"))) || !(dzp=fzp_gzopen(dst, "wb"))) goto end; while(azp || bzp || anew || bnew) { if(azp && asb && !anew) { switch(get_next_dindex(&anew, asb, azp)) { case -1: goto end; case 1: fzp_close(&azp); // Finished OK. } } if(bzp && bsb && !bnew) { switch(get_next_dindex(&bnew, bsb, bzp)) { case -1: goto end; case 1: fzp_close(&bzp); // Finished OK. } } if(anew && !bnew) { if(gzprintf_dindex(dzp, anew)) goto end; free_v((void **)&anew); } else if(!anew && bnew) { if(gzprintf_dindex(dzp, bnew)) goto end; free_v((void **)&bnew); } else if(!anew && !bnew) { continue; } else if(*anew==*bnew) { // They were the same - write the new one. if(gzprintf_dindex(dzp, bnew)) goto end; free_v((void **)&anew); free_v((void **)&bnew); } else if(*anew<*bnew) { if(gzprintf_dindex(dzp, anew)) goto end; free_v((void **)&anew); } else { if(gzprintf_dindex(dzp, bnew)) goto end; free_v((void **)&bnew); } } if(fzp_close(&dzp)) { logp("Error closing %s in %s\n", tmpfile, __func__); goto end; } ret=0; end: fzp_close(&azp); fzp_close(&bzp); fzp_close(&dzp); sbuf_free(&asb); sbuf_free(&bsb); free_v((void **)&anew); free_v((void **)&bnew); return ret; }
static #endif int merge_sparse_indexes(const char *dst, const char *srca, const char *srcb) { int fcmp; int ret=-1; struct sbuf *asb=NULL; struct sbuf *bsb=NULL; uint64_t *afingerprints=NULL; uint64_t *bfingerprints=NULL; size_t aflen=0; size_t bflen=0; struct fzp *azp=NULL; struct fzp *bzp=NULL; struct fzp *dzp=NULL; struct hooks *anew=NULL; struct hooks *bnew=NULL; char *apath=NULL; char *bpath=NULL; if(!(asb=sbuf_alloc(PROTO_2)) || (srcb && !(bsb=sbuf_alloc(PROTO_2)))) goto end; if(build_path_w(dst)) goto end; if((srca && !(azp=fzp_gzopen(srca, "rb"))) || (srcb && !(bzp=fzp_gzopen(srcb, "rb"))) || !(dzp=fzp_gzopen(dst, "wb"))) goto end; while(azp || bzp || anew || bnew) { if(azp && asb && !anew) { switch(get_next_set_of_hooks(&anew, asb, azp, &apath, &afingerprints, &aflen)) { case -1: goto end; case 1: fzp_close(&azp); // Finished OK. } } if(bzp && bsb && !bnew) { switch(get_next_set_of_hooks(&bnew, bsb, bzp, &bpath, &bfingerprints, &bflen)) { case -1: goto end; case 1: fzp_close(&bzp); // Finished OK. } } if(anew && !bnew) { if(gzprintf_hooks(dzp, anew)) goto end; hooks_free(&anew); } else if(!anew && bnew) { if(gzprintf_hooks(dzp, bnew)) goto end; hooks_free(&bnew); } else if(!anew && !bnew) { continue; } else if(!(fcmp=hookscmp(anew, bnew))) { // They were the same - write the new one. if(gzprintf_hooks(dzp, bnew)) goto end; hooks_free(&anew); hooks_free(&bnew); } else if(fcmp<0) { if(gzprintf_hooks(dzp, anew)) goto end; hooks_free(&anew); } else { if(gzprintf_hooks(dzp, bnew)) goto end; hooks_free(&bnew); } } if(fzp_close(&dzp)) { logp("Error closing %s in %s\n", tmpfile, __func__); goto end; } ret=0; end: fzp_close(&azp); fzp_close(&bzp); fzp_close(&dzp); sbuf_free(&asb); sbuf_free(&bsb); hooks_free(&anew); hooks_free(&bnew); free_v((void **)&afingerprints); free_v((void **)&bfingerprints); free_w(&apath); free_w(&bpath); return ret; }