static int list_manifest(const char *fullpath, regex_t *regex, const char *browsedir, const char *client, struct cntr *p1cntr, struct cntr *cntr) { int ars=0; int ret=0; int quit=0; gzFile zp=NULL; struct sbuf mb; char *manifest=NULL; size_t bdlen=0; char *lastpath=NULL; init_sbuf(&mb); if(!(manifest=prepend_s(fullpath, "manifest.gz", strlen("manifest.gz")))) { log_and_send("out of memory"); return -1; } if(!(zp=gzopen_file(manifest, "rb"))) { log_and_send("could not open manifest"); free(manifest); return -1; } free(manifest); if(browsedir) bdlen=strlen(browsedir); while(!quit) { int show=0; //logp("list manifest loop\n"); // Need to parse while sending, to take note of the regex. free_sbuf(&mb); if((ars=sbuf_fill(NULL, zp, &mb, cntr))) { if(ars<0) ret=-1; // ars==1 means it ended ok. break; } //if(mb.path[mb.plen]=='\n') mb.path[mb.plen]='\0'; write_status(client, STATUS_LISTING, mb.path, p1cntr, cntr); if(browsedir) { int r; if((r=check_browsedir(browsedir, &(mb.path), bdlen, &lastpath))<0) { quit++; ret=-1; } if(!r) continue; show++; } else { if(check_regex(regex, mb.path)) show++; } if(show) { if(async_write(CMD_STAT, mb.statbuf, mb.slen) || async_write(mb.cmd, mb.path, mb.plen)) { quit++; ret=-1; } else if(sbuf_is_link(&mb) && async_write(mb.cmd, mb.linkto, mb.llen)) { quit++; ret=-1; } } } gzclose_fp(&zp); free_sbuf(&mb); if(lastpath) free(lastpath); return ret; }
static int maybe_delete_files_from_manifest(const char *manifest, const char *deletionsfile, struct config *cconf, struct cntr *cntr) { int ars=0; int ret=0; int pcmp=0; FILE *dfp=NULL; struct sbuf db; struct sbuf mb; gzFile nmzp=NULL; gzFile omzp=NULL; char *manifesttmp=NULL; struct stat statp; if(lstat(deletionsfile, &statp)) { // No deletions, no problem. return 0; } logp("Performing deletions on manifest\n"); if(!(manifesttmp=get_tmp_filename(manifest))) { ret=-1; goto end; } if(!(dfp=open_file(deletionsfile, "rb")) || !(omzp=gzopen_file(manifest, "rb")) || !(nmzp=gzopen_file(manifesttmp, comp_level(cconf)))) { ret=-1; goto end; } init_sbuf(&db); init_sbuf(&mb); while(omzp || dfp) { if(dfp && !db.path && (ars=sbuf_fill(dfp, NULL, &db, cntr))) { if(ars<0) { ret=-1; break; } // ars==1 means it ended ok. close_fp(&dfp); } if(omzp && !mb.path && (ars=sbuf_fill(NULL, omzp, &mb, cntr))) { if(ars<0) { ret=-1; break; } // ars==1 means it ended ok. gzclose_fp(&omzp); } if(mb.path && !db.path) { if(sbuf_to_manifest(&mb, NULL, nmzp)) { ret=-1; break; } free_sbuf(&mb); } else if(!mb.path && db.path) { free_sbuf(&db); } else if(!mb.path && !db.path) { continue; } else if(!(pcmp=sbuf_pathcmp(&mb, &db))) { // They were the same - do not write. free_sbuf(&mb); free_sbuf(&db); } else if(pcmp<0) { // Behind in manifest. Write. if(sbuf_to_manifest(&mb, NULL, nmzp)) { ret=-1; break; } free_sbuf(&mb); } else { // Behind in deletions file. Do not write. free_sbuf(&db); } } end: if(gzclose_fp(&nmzp)) { logp("error closing %s in maybe_delete_files_from_manifest\n", manifesttmp); ret=-1; } close_fp(&dfp); gzclose_fp(&omzp); free_sbuf(&db); free_sbuf(&mb); if(!ret) { unlink(deletionsfile); if(do_rename(manifesttmp, manifest)) { free(manifesttmp); return -1; } } if(manifesttmp) { unlink(manifesttmp); free(manifesttmp); } return ret; }
static int process_changed_file(struct sbuf *cb, struct sbuf *p1b, const char *currentdata, const char *datadirtmp, const char *deltmppath, int *resume_partial, struct cntr *cntr, struct config *cconf) { size_t blocklen=0; char *curpath=NULL; //logp("need to process changed file: %s (%s)\n", cb->path, cb->datapth); // Move datapth onto p1b. if(p1b->datapth) free(p1b->datapth); p1b->datapth=cb->datapth; cb->datapth=NULL; if(!(curpath=prepend_s(currentdata, p1b->datapth, strlen(p1b->datapth)))) { log_out_of_memory(__FUNCTION__); return -1; } if(dpth_is_compressed(cb->compression, curpath)) p1b->sigzp=gzopen_file(curpath, "rb"); else p1b->sigfp=open_file(curpath, "rb"); if(!p1b->sigzp && !p1b->sigfp) { logp("could not open %s: %s\n", curpath, strerror(errno)); free(curpath); return -1; } if(*resume_partial && p1b->cmd==CMD_FILE && cconf->librsync) // compression? { if(resume_partial_changed_file(cb, p1b, currentdata, curpath, datadirtmp, deltmppath, cconf, cntr)) return -1; // Burp only transfers one file at a time, so // if there was an interruption, there is only // a possibility of one partial file to resume. *resume_partial=0; } free(curpath); blocklen=get_librsync_block_len(cb->endfile); if(!(p1b->sigjob=rs_sig_begin(blocklen, RS_DEFAULT_STRONG_LEN))) { logp("could not start signature job.\n"); return -1; } //logp("sig begin: %s\n", p1b->datapth); if(!(p1b->infb=rs_filebuf_new(NULL, p1b->sigfp, p1b->sigzp, -1, blocklen, -1, cntr))) { logp("could not rs_filebuf_new for infb.\n"); return -1; } if(!(p1b->outfb=rs_filebuf_new(NULL, NULL, NULL, async_get_fd(), ASYNC_BUF_LEN, -1, cntr))) { logp("could not rs_filebuf_new for in_outfb.\n"); return -1; } // Flag the things that need to be sent (to the client) p1b->senddatapth++; p1b->sendstat++; p1b->sendpath++; //logp("sending sig for %s\n", p1b->path); //logp("(%s)\n", p1b->datapth); return 0; }
static int resume_partial_new_file(struct sbuf *p1b, struct cntr *cntr, const char *currentdata, const char *datadirtmp, const char *deltmppath, struct dpth *dpth, struct config *cconf) { int ret=0; int junk=0; struct sbuf cb; char *rpath=NULL; int istreedata=0; struct stat statp; char *partial=NULL; char *partialdir=NULL; char *zdeltmp=NULL; // It does not matter what this checksum is set to. // This is just to get an endfile string in the format that // process_changed_file expects. unsigned char checksum[18]="0123456789ABCDEF"; // Need to set up a fake current sbuf. init_sbuf(&cb); cb.cmd=p1b->cmd; cb.compression=p1b->compression; cb.path=strdup(p1b->path); cb.statbuf=strdup(p1b->statbuf); if(!(rpath=set_new_datapth(&cb, datadirtmp, dpth, &istreedata, cconf))) { ret=-1; goto end; } if(!(partialdir=prepend_s(datadirtmp, "p", strlen("p"))) || !(partial=prepend_s(partialdir, cb.datapth, strlen(cb.datapth))) || build_path(partialdir, cb.datapth, strlen(cb.datapth), &partial, partialdir)) { ret=-1; goto end; } if(!lstat(partial, &statp) && S_ISREG(statp.st_mode)) { // A previous resume was going on. // Need to concatenate the possible delta onto the partial // file. FILE *dfp=NULL; gzFile dzp=NULL; logp("Resume previously resumed partial new file: %s %s\n", cb.path, rpath); if(!(cb.endfile=strdup( get_endfile_str(statp.st_size, checksum)))) { ret=-1; goto end; } if(cb.compression) { // Recreate partial, in case it was only partially // written and consequently has gz errors. if(!(zdeltmp=prepend(deltmppath, ".z", strlen(".z"), 0 /* no slash */)) || !(dzp=gzopen_file(zdeltmp, "wb")) || copy_gzpath_to_gzFile(partial, dzp) || do_rename(zdeltmp, partial)) { ret=-1; goto end; } } else { // Just append to the existing one. if(!(dfp=open_file(partial, "ab"))) { ret=-1; goto end; } } if(!lstat(deltmppath, &statp) && S_ISREG(statp.st_mode)) { if(cb.compression) { if(copy_gzpath_to_gzFile(deltmppath, dzp)) { ret=-1; goto end; } } else { if(copy_path_to_File(deltmppath, dfp)) { ret=-1; goto end; } } } if(dfp && close_fp(&dfp)) { ret=-1; goto end; } if(dzp && gzclose_fp(&dzp)) { ret=-1; goto end; } if(process_changed_file(&cb, p1b, partialdir, NULL, NULL, &junk /* resume_partial=0 */, cntr, cconf)) { ret=-1; goto end; } if(!istreedata) incr_dpth(dpth, cconf); goto end; } logp("Resume partial new file: %s %s\n", cb.path, rpath); if(!lstat(rpath, &statp) && S_ISREG(statp.st_mode)) { if(!(cb.endfile=strdup( get_endfile_str(statp.st_size, checksum)))) { ret=-1; goto end; } // If compression is on, be careful with gzip unexpected // end of file errors. // Otherwise, just rename the whole file. unlink(partial); if(cb.compression) { if(copy_gzpath_to_gzpath(rpath, partial)) { logp("Error in copy_gzpath_to_gzpath\n"); ret=-1; goto end; } // delete the original. if(unlink(rpath)) { logp("Failed to unlink %s: %s\n", rpath, strerror(errno)); return -1; } } else { if(do_rename(rpath, partial)) { ret=-1; goto end; } } // So, we have created a new directory beginning with 'p', // and moved the partial download to it. // We can now use the partial file as the basis of a librsync // transfer. if(process_changed_file(&cb, p1b, partialdir, NULL, NULL, &junk /* resume_partial=0 */, cntr, cconf)) { ret=-1; goto end; } if(!istreedata) incr_dpth(dpth, cconf); goto end; } logp("Actually, no - just treat it as completely new\n"); end: if(rpath) free(rpath); if(partialdir) free(partialdir); if(partial) free(partial); if(zdeltmp) free(zdeltmp); free_sbuf(&cb); return ret; }
int backup_phase2_server(gzFile *cmanfp, const char *phase1data, const char *phase2data, const char *unchangeddata, const char *datadirtmp, struct dpth *dpth, const char *currentdata, const char *working, const char *client, struct cntr *p1cntr, int resume, struct cntr *cntr, struct config *cconf) { int ars=0; int ret=0; gzFile p1zp=NULL; char *deltmppath=NULL; char *last_requested=NULL; // Where to write phase2data. // Data is not getting written to a compressed file. // This is important for recovery if the power goes. FILE *p2fp=NULL; // unchanged data FILE *ucfp=NULL; int resume_partial=resume; struct sbuf cb; // file list in current manifest struct sbuf p1b; // file list from client struct sbuf rb; // receiving file from client init_sbuf(&cb); init_sbuf(&p1b); init_sbuf(&rb); if(!(p1zp=gzopen_file(phase1data, "rb"))) goto error; // Open in read+write mode, so that they can be read through if // we need to resume. // First, open them in a+ mode, so that they will be created if they // do not exist. if(!(ucfp=open_file(unchangeddata, "a+b"))) goto error; if(!(p2fp=open_file(phase2data, "a+b"))) goto error; close_fp(&ucfp); close_fp(&p2fp); if(!(ucfp=open_file(unchangeddata, "r+b"))) goto error; if(!(p2fp=open_file(phase2data, "r+b"))) goto error; if(resume && do_resume(p1zp, p2fp, ucfp, dpth, cconf, client, p1cntr, cntr)) goto error; logp("Begin phase2 (receive file data)\n"); if(!(deltmppath=prepend_s(working, "delta.tmp", strlen("delta.tmp")))) goto error; while(1) { int sts=0; // logp("in loop, %s %s %c\n", // *cmanfp?"got cmanfp":"no cmanfp", // rb.path?:"no rb.path", rb.path?'X':rb.cmd); if(rb.path) write_status(client, STATUS_BACKUP, rb.path, p1cntr, cntr); else write_status(client, STATUS_BACKUP, p1b.path, p1cntr, cntr); if((last_requested || !p1zp || writebuflen) && (ars=do_stuff_to_receive(&rb, p2fp, datadirtmp, dpth, working, &last_requested, deltmppath, cntr, cconf))) { if(ars<0) goto error; // 1 means ok. break; } if((sts=do_stuff_to_send(&p1b, &last_requested))<0) goto error; if(!sts && p1zp) { free_sbuf(&p1b); if((ars=sbuf_fill_phase1(NULL, p1zp, &p1b, cntr))) { if(ars<0) goto error; // ars==1 means it ended ok. gzclose_fp(&p1zp); //logp("ended OK - write phase2end"); if(async_write_str(CMD_GEN, "backupphase2end")) goto error; } //logp("check: %s\n", p1b.path); if(!*cmanfp) { // No old manifest, need to ask for a new file. //logp("no cmanfp\n"); if(process_new(&p1b, ucfp, currentdata, datadirtmp, deltmppath, dpth, &resume_partial, cntr, cconf)) goto error; } else { // Have an old manifest, look for it there. // Might already have it, or be ahead in the old // manifest. if(cb.path) { if((ars=maybe_process_file(&cb, &p1b, ucfp, currentdata, datadirtmp, deltmppath, dpth, &resume_partial, cntr, cconf))) { if(ars<0) goto error; // Do not free it - need to send stuff. continue; } //free_sbuf(&p1b); } while(*cmanfp) { free_sbuf(&cb); if((ars=sbuf_fill(NULL, *cmanfp, &cb, cntr))) { // ars==1 means it ended ok. if(ars<0) goto error; gzclose_fp(cmanfp); //logp("ran out of current manifest\n"); if(process_new(&p1b, ucfp, currentdata, datadirtmp, deltmppath, dpth, &resume_partial, cntr, cconf)) goto error; break; } //logp("against: %s\n", cb.path); if((ars=maybe_process_file(&cb, &p1b, ucfp, currentdata, datadirtmp, deltmppath, dpth, &resume_partial, cntr, cconf))) { if(ars<0) goto error; // Do not free it - need to send stuff. break; } } } } } goto end; error: ret=-1; end: if(close_fp(&p2fp)) { logp("error closing %s in backup_phase2_server\n", phase2data); ret=-1; } if(close_fp(&ucfp)) { logp("error closing %s in backup_phase2_server\n", unchangeddata); ret=-1; } free(deltmppath); free_sbuf(&cb); free_sbuf(&p1b); free_sbuf(&rb); gzclose_fp(&p1zp); if(!ret) unlink(phase1data); logp("End phase2 (receive file data)\n"); return ret; }
// TODO: Some of the repeated code in this can be factored out. static int resume_partial_changed_file(struct sbuf *cb, struct sbuf *p1b, const char *currentdata, const char *curpath, const char *datadirtmp, const char *deltmppath, struct config *cconf, struct cntr *cntr) { int ret=0; int istreedata=0; struct stat dstatp; struct stat cstatp; char *partial=NULL; char *partialdir=NULL; char *zdeltmp=NULL; struct sbuf xb; init_sbuf(&xb); xb.cmd=cb->cmd; xb.compression=cb->compression; xb.path=strdup(cb->path); xb.statbuf=strdup(cb->statbuf); xb.datapth=strdup(p1b->datapth); xb.endfile=strdup(cb->endfile); logp("Resume partial changed file: %s\n", xb.path); if(!lstat(deltmppath, &dstatp) && S_ISREG(dstatp.st_mode) && !lstat(curpath, &cstatp) && S_ISREG(cstatp.st_mode)) { int junk=0; gzFile dzp=NULL; FILE *dfp=NULL; struct stat pstatp; if(!(partialdir=prepend_s(datadirtmp, "p", strlen("p"))) || !(partial=prepend_s(partialdir, xb.datapth, strlen(xb.datapth))) || build_path(partialdir, xb.datapth, strlen(xb.datapth), &partial, partialdir)) { ret=-1; goto end; } if(!lstat(partial, &pstatp)) { if(!S_ISREG(pstatp.st_mode)) { logp("%s is not a regular file\n", partial); goto actuallyno; } if(pstatp.st_size>cstatp.st_size) { // Looks like a previously resumed file. if(xb.compression) { // Need to read and recreate it, in // case it was not fully created. if(!(zdeltmp=prepend(deltmppath, ".z", strlen(".z"), 0 /* no slash */)) || !(dzp=gzopen_file(zdeltmp, "wb")) || copy_gzpath_to_gzFile(partial, dzp) || do_rename(zdeltmp, partial)) { ret=-1; goto end; } } else { // Append to the existing one. if(!(dfp=open_file(partial, "ab"))) { ret=-1; goto end; } } } else { unlink(partial); // Copy the whole of p1b->sigfp/sigzp to // partial. if(xb.compression) { if(!(dzp=gzopen_file(partial, "wb")) || copy_gzFile_to_gzFile(p1b->sigzp, dzp)) { ret=-1; goto end; } } else { if(!(dfp=open_file(partial, "wb")) || copy_File_to_File(p1b->sigfp, dfp)) { ret=-1; goto end; } } } // Now, copy the whole of deltmppath onto partial. // dzp or dfp will be open by this point. if(xb.compression) { if(copy_gzpath_to_gzFile(deltmppath, dzp)) { ret=-1; goto end; } } else { if(copy_path_to_File(deltmppath, dfp)) { ret=-1; goto end; } } } else { // Copy the whole of p1b->sigfp/sigzp onto partial. // Copy the whole of deltmppath onto partial. if(xb.compression) { // There is no partial, this creates it. if(!(dzp=gzopen_file(partial, "wb")) || copy_gzFile_to_gzFile(p1b->sigzp, dzp)) { ret=-1; goto end; } } else { // There is no partial, this creates it. if(!(dfp=open_file(partial, "wb")) || copy_File_to_File(p1b->sigfp, dfp)) { ret=-1; goto end; } } if(xb.compression) { if(copy_gzpath_to_gzFile(deltmppath, dzp)) { ret=-1; goto end; } } else { if(copy_path_to_File(deltmppath, dfp)) { ret=-1; goto end; } } } if(dfp && close_fp(&dfp)) { ret=-1; goto end; } if(dzp && gzclose_fp(&dzp)) { ret=-1; goto end; } // Use partial as the basis for a librsync transfer. // So, we have created a new directory beginning with 'p', // and moved the partial download to it. // We can now use the partial file as the basis of a librsync // transfer. if(process_changed_file(&xb, p1b, partialdir, NULL, NULL, &junk /* resume_partial=0 */, cntr, cconf)) { ret=-1; goto end; } goto end; } actuallyno: logp("Actually, no - just forget the previous delta\n"); end: if(partialdir) free(partialdir); if(partial) free(partial); if(zdeltmp) free(zdeltmp); free_sbuf(&xb); return ret; }
static int process_changed_file(struct asfd *asfd, struct sdirs *sdirs, struct conf *cconf, struct sbuf *cb, struct sbuf *p1b, const char *adir) { size_t blocklen=0; char *curpath=NULL; //logp("need to process changed file: %s (%s)\n", cb->path, cb->datapth); // Move datapth onto p1b. iobuf_copy(&p1b->burp1->datapth, &cb->burp1->datapth); cb->burp1->datapth.buf=NULL; if(!(curpath=prepend_s(adir, p1b->burp1->datapth.buf))) { log_out_of_memory(__func__); return -1; } if(dpthl_is_compressed(cb->compression, curpath)) p1b->burp1->sigzp=gzopen_file(curpath, "rb"); else p1b->burp1->sigfp=open_file(curpath, "rb"); if(!p1b->burp1->sigzp && !p1b->burp1->sigfp) { logp("could not open %s: %s\n", curpath, strerror(errno)); free(curpath); return -1; } free(curpath); blocklen=get_librsync_block_len(cb->burp1->endfile.buf); if(!(p1b->burp1->sigjob=rs_sig_begin(blocklen, RS_DEFAULT_STRONG_LEN))) { logp("could not start signature job.\n"); return -1; } //logp("sig begin: %s\n", p1b->burp1->datapth.buf); if(!(p1b->burp1->infb=rs_filebuf_new(asfd, NULL, p1b->burp1->sigfp, p1b->burp1->sigzp, -1, blocklen, -1, cconf->cntr))) { logp("could not rs_filebuf_new for infb.\n"); return -1; } if(!(p1b->burp1->outfb=rs_filebuf_new(asfd, NULL, NULL, NULL, asfd->fd, ASYNC_BUF_LEN, -1, cconf->cntr))) { logp("could not rs_filebuf_new for in_outfb.\n"); return -1; } // Flag the things that need to be sent (to the client) p1b->flags |= SBUFL_SEND_DATAPTH; p1b->flags |= SBUFL_SEND_STAT; p1b->flags |= SBUFL_SEND_PATH; //logp("sending sig for %s\n", p1b->path); //logp("(%s)\n", p1b->datapth); return 0; }
int backup_phase2_server(struct asfd *asfd, struct sdirs *sdirs, struct conf *cconf, gzFile *cmanfp, struct dpthl *dpthl, int resume) { int ars=0; int ret=0; gzFile p1zp=NULL; char *deltmppath=NULL; char *last_requested=NULL; // Where to write phase2data. // Data is not getting written to a compressed file. // This is important for recovery if the power goes. FILE *p2fp=NULL; // unchanged data FILE *ucfp=NULL; 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 if(!(cb=sbuf_alloc(cconf)) || !(p1b=sbuf_alloc(cconf)) || !(rb=sbuf_alloc(cconf))) goto error; if(!(p1zp=gzopen_file(sdirs->phase1data, "rb"))) goto error; // Open in read+write mode, so that they can be read through if // we need to resume. // First, open them in a+ mode, so that they will be created if they // do not exist. if(!(ucfp=open_file(sdirs->unchangeddata, "a+b")) || !(p2fp=open_file(sdirs->phase2data, "a+b"))) goto error; close_fp(&ucfp); close_fp(&p2fp); if(!(ucfp=open_file(sdirs->unchangeddata, "r+b")) || !(p2fp=open_file(sdirs->phase2data, "r+b"))) goto error; if(resume && do_resume(p1zp, p2fp, ucfp, dpthl, cconf)) goto error; logp("Begin phase2 (receive file data)\n"); while(1) { int sts=0; //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(STATUS_BACKUP, rb->path.buf?rb->path.buf:p1b->path.buf, cconf)) goto error; if((last_requested || !p1zp || asfd->writebuflen) && (ars=do_stuff_to_receive(asfd, sdirs, cconf, rb, p2fp, dpthl, &last_requested))) { if(ars<0) goto error; // 1 means ok. break; } if((sts=do_stuff_to_send(asfd, p1b, &last_requested))<0) goto error; if(!sts && p1zp) { sbuf_free_content(p1b); if((ars=sbufl_fill_phase1(p1b, NULL, p1zp, cconf->cntr))) { if(ars<0) goto error; // ars==1 means it ended ok. gzclose_fp(&p1zp); //logp("ended OK - write phase2end"); if(asfd->write_str(asfd, CMD_GEN, "backupphase2end")) goto error; } //logp("check: %s\n", p1b.path); if(!*cmanfp) { // No old manifest, need to ask for a new file. //logp("no cmanfp\n"); if(process_new(sdirs, cconf, p1b, ucfp, dpthl)) goto error; } else { // Have an old manifest, look for it there. // Might already have it, or be ahead in the old // manifest. if(cb->path.buf) { if((ars=maybe_process_file(asfd, sdirs, cconf, cb, p1b, ucfp, dpthl))) { if(ars<0) goto error; // Do not free it - need to send stuff. continue; } //free_sbufl(&p1b); } while(*cmanfp) { sbuf_free_content(cb); if((ars=sbufl_fill(cb, asfd, NULL, *cmanfp, cconf->cntr))) { // ars==1 means it ended ok. if(ars<0) goto error; gzclose_fp(cmanfp); //logp("ran out of current manifest\n"); if(process_new(sdirs, cconf, p1b, ucfp, dpthl)) goto error; break; } //logp("against: %s\n", cb.path); if((ars=maybe_process_file(asfd, sdirs, cconf, cb, p1b, ucfp, dpthl))) { if(ars<0) goto error; // Do not free it - need to send stuff. break; } } } } } goto end; error: ret=-1; end: if(close_fp(&p2fp)) { logp("error closing %s in backup_phase2_server\n", sdirs->phase2data); ret=-1; } if(close_fp(&ucfp)) { logp("error closing %s in backup_phase2_server\n", sdirs->unchangeddata); ret=-1; } free(deltmppath); sbuf_free(cb); sbuf_free(p1b); sbuf_free(rb); gzclose_fp(&p1zp); if(!ret) unlink(sdirs->phase1data); logp("End phase2 (receive file data)\n"); return ret; }
int backup_phase1_server(struct asfd *asfd, struct sdirs *sdirs, struct conf *conf) { int ars=0; int ret=-1; struct sbuf *sb=NULL; gzFile p1zp=NULL; char *phase1tmp=NULL; logp("Begin phase1 (file system scan)\n"); if(!(phase1tmp=get_tmp_filename(sdirs->phase1data))) goto end; if(!(p1zp=gzopen_file(phase1tmp, comp_level(conf)))) goto end; if(!(sb=sbuf_alloc(conf))) goto end; while(1) { sbuf_free_content(sb); if(conf->protocol==PROTO_BURP1) ars=sbufl_fill(sb, asfd, NULL, NULL, conf->cntr); else ars=sbuf_fill(sb, asfd, NULL, NULL, NULL, conf); if(ars) { if(ars<0) goto end; //ars==1 means it ended ok. // Last thing the client sends is 'backupphase2', and // it wants an 'ok' reply. if(asfd->write_str(asfd, CMD_GEN, "ok") || send_msg_zp(p1zp, CMD_GEN, "phase1end", strlen("phase1end"))) goto end; break; } if(write_status(STATUS_SCANNING, sb->path.buf, conf) || sbufl_to_manifest_phase1(sb, NULL, p1zp)) goto end; cntr_add_phase1(conf->cntr, sb->path.cmd, 0); if(sb->path.cmd==CMD_FILE || sb->path.cmd==CMD_ENC_FILE || sb->path.cmd==CMD_METADATA || sb->path.cmd==CMD_ENC_METADATA || sb->path.cmd==CMD_EFS_FILE) cntr_add_val(conf->cntr, CMD_BYTES_ESTIMATED, (unsigned long long)sb->statp.st_size, 0); } if(gzclose_fp(&p1zp)) { logp("error closing %s in backup_phase1_server\n", phase1tmp); goto end; } if(do_rename(phase1tmp, sdirs->phase1data)) goto end; //cntr_print(p1cntr, cntr, ACTION_BACKUP); logp("End phase1 (file system scan)\n"); ret=0; end: free(phase1tmp); gzclose_fp(&p1zp); sbuf_free(sb); 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(const char *finishing, const char *working, const char *manifest, const char *current, const char *currentdata, const char *datadir, const char *datadirtmp, const char *deletionsfile, struct config *cconf, const char *client, int hardlinked, unsigned long bno, struct cntr *p1cntr, struct cntr *cntr) { int ret=0; int ars=0; char *datapth=NULL; char *tmpman=NULL; struct stat statp; char *deltabdir=NULL; char *deltafdir=NULL; char *sigpath=NULL; gzFile zp=NULL; struct sbuf sb; FILE *delfp=NULL; logp("Doing the atomic data jiggle...\n"); if(!(tmpman=get_tmp_filename(manifest))) return -1; if(lstat(manifest, &statp)) { // Manifest does not exist - maybe the server was killed before // it could be renamed. logp("%s did not exist - trying %s\n", manifest, tmpman); do_rename(tmpman, manifest); } free(tmpman); if(!(zp=gzopen_file(manifest, "rb"))) return -1; if(!(deltabdir=prepend_s(current, "deltas.reverse", strlen("deltas.reverse"))) || !(deltafdir=prepend_s(finishing, "deltas.forward", strlen("deltas.forward"))) || !(sigpath=prepend_s(current, "sig.tmp", strlen("sig.tmp")))) { logp("out of memory\n"); gzclose_fp(&zp); return -1; } mkdir(datadir, 0777); init_sbuf(&sb); while(!(ars=sbuf_fill(NULL, zp, &sb, cntr))) { if(sb.datapth) { write_status(client, STATUS_SHUFFLING, sb.datapth, p1cntr, cntr); if((ret=jiggle(sb.datapth, currentdata, datadirtmp, datadir, deltabdir, deltafdir, sigpath, sb.endfile, deletionsfile, &delfp, &sb, hardlinked, sb.compression, cntr, cconf))) break; } free_sbuf(&sb); } if(!ret) { if(ars>0) ret=0; else ret=-1; } if(close_fp(&delfp)) { logp("error closing %s in atomic_data_jiggle\n", deletionsfile); ret=-1; } gzclose_fp(&zp); if(maybe_delete_files_from_manifest(manifest, deletionsfile, cconf, cntr)) ret=-1; // Remove the temporary data directory, we have probably removed // useful files from it. sync(); // try to help CIFS recursive_delete(deltafdir, NULL, FALSE /* do not del files */); if(deltabdir) free(deltabdir); if(deltafdir) free(deltafdir); if(sigpath) free(sigpath); if(datapth) free(datapth); return ret; }
static int make_rev_delta(const char *src, const char *sig, const char *del, int compression, struct cntr *cntr, struct config *cconf) { gzFile srczp=NULL; FILE *srcfp=NULL; FILE *sigp=NULL; rs_result result; rs_signature_t *sumset=NULL; //logp("make rev delta: %s %s %s\n", src, sig, del); if(!(sigp=open_file(sig, "rb"))) return -1; if((result=rs_loadsig_file(sigp, &sumset, NULL)) || (result=rs_build_hash_table(sumset))) { fclose(sigp); rs_free_sumset(sumset); return result; } fclose(sigp); //logp("make rev deltb: %s %s %s\n", src, sig, del); if(dpth_is_compressed(compression, src)) srczp=gzopen_file(src, "rb"); else srcfp=open_file(src, "rb"); if(!srczp && !srcfp) { rs_free_sumset(sumset); return -1; } if(cconf->compression) { gzFile delzp=NULL; if(!(delzp=gzopen_file(del, comp_level(cconf)))) { gzclose_fp(&srczp); close_fp(&srcfp); rs_free_sumset(sumset); return -1; } result=rs_delta_gzfile(sumset, srcfp, srczp, NULL, delzp, NULL, cntr); if(gzclose_fp(&delzp)) { logp("error closing %s in make_rev_delta\n", del); result=RS_IO_ERROR; } } else { FILE *delfp=NULL; if(!(delfp=open_file(del, "wb"))) { gzclose_fp(&srczp); close_fp(&srcfp); rs_free_sumset(sumset); return -1; } result=rs_delta_gzfile(sumset, srcfp, srczp, delfp, NULL, NULL, cntr); if(close_fp(&delfp)) { logp("error closing %s in make_rev_delta\n", del); gzclose_fp(&srczp); close_fp(&srcfp); rs_free_sumset(sumset); return -1; } } rs_free_sumset(sumset); gzclose_fp(&srczp); close_fp(&srcfp); return result; }