// Return -1 for error, 0 for stuff read OK, 1 for end of files. int manio_read_with_blk(struct manio *manio, struct sbuf *sb, struct blk *blk, struct sdirs *sdirs) { while(1) { if(!manio->fzp) { if(manio_open_next_fpath(manio)) goto error; if(!manio->fzp) return 1; // No more files to read. } switch(sbuf_fill_from_file(sb, manio->fzp, blk, sdirs?sdirs->data:NULL)) { case 0: return 0; // Got something. case 1: break; // Keep going. default: goto error; // Error. } // Reached the end of the current file. // Maybe there is another file to continue with. if(sort_and_write_hooks_and_dindex(manio) || fzp_close(&manio->fzp)) goto error; if(is_single_file(manio)) return 1; } error: return -1; }
static int setup_cntr(struct asfd *asfd, const char *manifest, regex_t *regex, int srestore, enum action act, char status, struct conf **cconfs) { int ars=0; int ret=-1; struct fzp *fzp=NULL; struct sbuf *sb=NULL; struct cntr *cntr=get_cntr(cconfs); // FIX THIS: this is only trying to work for protocol1. if(get_protocol(cconfs)!=PROTO_1) return 0; if(!(sb=sbuf_alloc(PROTO_1))) goto end; if(!(fzp=fzp_gzopen(manifest, "rb"))) { log_and_send(asfd, "could not open manifest"); goto end; } while(1) { if((ars=sbuf_fill_from_file(sb, fzp, NULL, NULL))) { if(ars<0) goto end; // ars==1 means end ok break; } else { if(want_to_restore(srestore, sb, regex, cconfs)) { cntr_add_phase1(cntr, sb->path.cmd, 0); if(sb->endfile.buf) cntr_add_val(cntr, CMD_BYTES_ESTIMATED, strtoull(sb->endfile.buf, NULL, 10), 0); } } sbuf_free_content(sb); } ret=0; end: sbuf_free(&sb); fzp_close(&fzp); return ret; }
// Return 0 for OK, -1 for error, 1 for finished reading the file. static int get_next_set_of_hooks(struct hooks **hnew, struct sbuf *sb, struct fzp *spzp, char **path, uint64_t **fingerprints, size_t *len) { struct blk blk; while(1) { switch(sbuf_fill_from_file(sb, spzp, NULL, NULL)) { case -1: goto error; case 1: // Reached the end. if(hooks_alloc(hnew, path, fingerprints, len)) goto error; return 1; } if(sb->path.cmd==CMD_MANIFEST) { if(hooks_alloc(hnew, path, fingerprints, len)) break; *path=sb->path.buf; sb->path.buf=NULL; sbuf_free_content(sb); if(*hnew) return 0; } else if(sb->path.cmd==CMD_FINGERPRINT) { if(!(*fingerprints=(uint64_t *)realloc_w(*fingerprints, ((*len)+1)*sizeof(uint64_t), __func__))) goto error; if(blk_set_from_iobuf_fingerprint(&blk, &sb->path)) goto error; (*fingerprints)[(*len)++]=blk.fingerprint; sbuf_free_content(sb); } else { iobuf_log_unexpected(&sb->path, __func__); break; } } error: sbuf_free_content(sb); return -1; }
// This deals with reading in the sparse index, as well as actual candidate // manifests. enum cand_ret candidate_load(struct candidate *candidate, const char *path, struct scores *scores) { enum cand_ret ret=CAND_RET_PERM; struct fzp *fzp=NULL; struct sbuf *sb=NULL; struct blk *blk=NULL; if(!(sb=sbuf_alloc(PROTO_2)) || !(blk=blk_alloc())) { ret=CAND_RET_PERM; goto error; } if(!(fzp=fzp_gzopen(path, "rb"))) { ret=CAND_RET_TEMP; goto error; } while(fzp) { sbuf_free_content(sb); switch(sbuf_fill_from_file(sb, fzp, blk, NULL)) { case 1: goto end; case -1: logp("Error reading %s in %s, pos %d\n", path, __func__, fzp_tell(fzp)); ret=CAND_RET_TEMP; goto error; } if(blk_fingerprint_is_hook(blk)) { if(sparse_add_candidate(&blk->fingerprint, candidate)) { ret=CAND_RET_PERM; goto error; } } else if(sb->path.cmd==CMD_MANIFEST) { if(!(candidate=candidates_add_new())) { ret=CAND_RET_PERM; goto error; } candidate->path=sb->path.buf; sb->path.buf=NULL; } blk->fingerprint=0; } end: if(scores_grow(scores, candidates_len)) { ret=CAND_RET_PERM; goto error; } candidates_set_score_pointers(candidates, candidates_len, scores); scores_reset(scores); //logp("Now have %d candidates\n", (int)candidates_len); ret=CAND_RET_OK; error: fzp_close(&fzp); sbuf_free(&sb); blk_free(&blk); return ret; }
/* Need to make all the stuff that this does atomic so that existing backups never get broken, even if somebody turns the power off on the server. */ static int atomic_data_jiggle(struct sdirs *sdirs, struct fdirs *fdirs, int hardlinked_current, struct conf **cconfs) { int ret=-1; char *datapth=NULL; char *tmpman=NULL; struct stat statp; char *deltabdir=NULL; char *deltafdir=NULL; char *sigpath=NULL; struct fzp *zp=NULL; struct sbuf *sb=NULL; struct fzp *delfp=NULL; logp("Doing the atomic data jiggle...\n"); if(!(tmpman=get_tmp_filename(fdirs->manifest))) goto error; if(lstat(fdirs->manifest, &statp)) { // Manifest does not exist - maybe the server was killed before // it could be renamed. logp("%s did not exist - trying %s\n", fdirs->manifest, tmpman); // Rename race condition is of no consequence, because manifest // already does not exist. do_rename(tmpman, fdirs->manifest); } if(!(zp=fzp_gzopen(fdirs->manifest, "rb"))) goto error; if(!(deltabdir=prepend_s(fdirs->currentdup, "deltas.reverse")) || !(deltafdir=prepend_s(sdirs->finishing, "deltas.forward")) || !(sigpath=prepend_s(fdirs->currentdup, "sig.tmp")) || !(sb=sbuf_alloc(PROTO_1))) { log_out_of_memory(__func__); goto error; } mkdir(fdirs->datadir, 0777); while(1) { switch(sbuf_fill_from_file(sb, zp, NULL, NULL)) { case 0: break; case 1: goto end; default: goto error; } if(sb->protocol1->datapth.buf) { if(write_status(CNTR_STATUS_SHUFFLING, sb->protocol1->datapth.buf, get_cntr(cconfs)) || jiggle(sdirs, fdirs, sb, hardlinked_current, deltabdir, deltafdir, sigpath, &delfp, cconfs)) goto error; } sbuf_free_content(sb); } end: if(fzp_close(&delfp)) { logp("error closing %s in atomic_data_jiggle\n", fdirs->deletionsfile); goto error; } if(maybe_delete_files_from_manifest(tmpman, fdirs, cconfs)) goto error; // Remove the temporary data directory, we have probably removed // useful files from it. recursive_delete_dirs_only(deltafdir); ret=0; error: fzp_close(&zp); fzp_close(&delfp); sbuf_free(&sb); free_w(&deltabdir); free_w(&deltafdir); free_w(&sigpath); free_w(&datapth); free_w(&tmpman); return ret; }
static int maybe_delete_files_from_manifest(const char *manifesttmp, struct fdirs *fdirs, struct conf **cconfs) { int ars=0; int ret=-1; int pcmp=0; struct fzp *dfp=NULL; struct fzp *nmzp=NULL; struct fzp *omzp=NULL; struct sbuf *db=NULL; struct sbuf *mb=NULL; struct stat statp; if(lstat(fdirs->deletionsfile, &statp)) // No deletions, no problem. return 0; logp("Performing deletions on manifest\n"); if(!(manifesttmp=get_tmp_filename(fdirs->manifest))) goto end; if(!(dfp=fzp_open(fdirs->deletionsfile, "rb")) || !(omzp=fzp_gzopen(fdirs->manifest, "rb")) || !(nmzp=fzp_gzopen(manifesttmp, comp_level(get_int(cconfs[OPT_COMPRESSION])))) || !(db=sbuf_alloc(PROTO_1)) || !(mb=sbuf_alloc(PROTO_1))) goto end; while(omzp || dfp) { if(dfp && !db->path.buf && (ars=sbuf_fill_from_file(db, dfp, NULL, NULL))) { if(ars<0) goto end; // ars==1 means it ended ok. fzp_close(&dfp); } if(omzp && !mb->path.buf && (ars=sbuf_fill_from_file(mb, omzp, NULL, NULL))) { if(ars<0) goto end; // ars==1 means it ended ok. fzp_close(&omzp); } if(mb->path.buf && !db->path.buf) { if(sbuf_to_manifest(mb, nmzp)) goto end; sbuf_free_content(mb); } else if(!mb->path.buf && db->path.buf) { sbuf_free_content(db); } else if(!mb->path.buf && !db->path.buf) { continue; } else if(!(pcmp=sbuf_pathcmp(mb, db))) { // They were the same - do not write. sbuf_free_content(mb); sbuf_free_content(db); } else if(pcmp<0) { // Behind in manifest. Write. if(sbuf_to_manifest(mb, nmzp)) goto end; sbuf_free_content(mb); } else { // Behind in deletions file. Do not write. sbuf_free_content(db); } } ret=0; end: if(fzp_close(&nmzp)) { logp("error closing %s in %s\n", manifesttmp, __func__); ret=-1; } fzp_close(&dfp); fzp_close(&omzp); sbuf_free(&db); sbuf_free(&mb); if(!ret) { unlink(fdirs->deletionsfile); // The rename race condition is not a problem here, as long // as manifesttmp is the same path as that generated in the // atomic data jiggle. if(do_rename(manifesttmp, fdirs->manifest)) return -1; } if(manifesttmp) unlink(manifesttmp); return ret; }