// return -1 for error, 0 for OK, 1 if the client wants to interrupt the // transfer. int do_quick_read(const char *datapth, struct cntr *cntr) { int r=0; char cmd; size_t len=0; char *buf=NULL; if(async_read_quick(&cmd, &buf, &len)) return -1; if(buf) { if(cmd==CMD_WARNING) { logp("WARNING: %s\n", buf); do_filecounter(cntr, cmd, 0); } else if(cmd==CMD_INTERRUPT) { // Client wants to interrupt - double check that // it is still talking about the file that we are // sending. if(datapth && !strcmp(buf, datapth)) r=1; } else { logp("unexpected cmd in quick read: %c:%s\n", cmd, buf); r=-1; } free(buf); } return r; }
static int restore_sbuf(struct sbuf *sb, struct bu *arr, int a, int i, const char *tmppath1, const char *tmppath2, enum action act, const char *client, char status, struct cntr *p1cntr, struct cntr *cntr, struct config *cconf) { //logp("%s: %s\n", act==ACTION_RESTORE?"restore":"verify", sb->path); write_status(client, status, sb->path, p1cntr, cntr); if((sb->datapth && async_write(CMD_DATAPTH, sb->datapth, strlen(sb->datapth))) || async_write(CMD_STAT, sb->statbuf, sb->slen)) return -1; else if(sb->cmd==CMD_FILE || sb->cmd==CMD_ENC_FILE || sb->cmd==CMD_METADATA || sb->cmd==CMD_ENC_METADATA || sb->cmd==CMD_EFS_FILE) { return restore_file(arr, a, i, sb->datapth, sb->path, tmppath1, tmppath2, act, sb->endfile, sb->cmd, sb->winattr, cntr, cconf); } else { if(async_write(sb->cmd, sb->path, sb->plen)) return -1; // If it is a link, send what // it points to. else if(sbuf_is_link(sb)) { if(async_write(sb->cmd, sb->linkto, sb->llen)) return -1; } do_filecounter(cntr, sb->cmd, 0); } return 0; }
static int maybe_send_extrameta(const char *path, char cmd, const char *attribs, struct cntr *p1cntr) { if(has_extrameta(path, cmd)) { if(async_write_str(CMD_STAT, attribs) || async_write_str(metasymbol, path)) return -1; do_filecounter(p1cntr, metasymbol, 1); } return 0; }
static int new_non_file(struct sbuf *p1b, FILE *ucfp, char cmd, struct cntr *cntr) { // Is something that does not need more data backed up. // Like a directory or a link or something like that. // Goes into the unchanged file, so that it does not end up out of // order with normal files, which has to wait around for their data // to turn up. if(sbuf_to_manifest(p1b, ucfp, NULL)) return -1; else do_filecounter(cntr, cmd, 0); free_sbuf(p1b); return 0; }
// should be in src/lib/log.c int logw(struct cntr *cntr, const char *fmt, ...) { int r=0; char buf[512]=""; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); if(doing_estimate) printf("\nWARNING: %s\n", buf); else { r=async_write_str(CMD_WARNING, buf); logp("WARNING: %s\n", buf); } va_end(ap); do_filecounter(cntr, CMD_WARNING, 1); return r; }
static int do_restore_end(enum action act, struct cntr *cntr) { char cmd; int ret=0; int quit=0; size_t len=0; if(async_write_str(CMD_GEN, "restoreend")) ret=-1; while(!ret && !quit) { char *buf=NULL; if(async_read(&cmd, &buf, &len)) { ret=-1; quit++; } else if(cmd==CMD_GEN && !strcmp(buf, "restoreend ok")) { logp("got restoreend ok\n"); quit++; } else if(cmd==CMD_WARNING) { logp("WARNING: %s\n", buf); do_filecounter(cntr, cmd, 0); } else if(cmd==CMD_INTERRUPT) { // ignore - client wanted to interrupt a file } else { logp("unexpected cmd from client at end of restore: %c:%s\n", cmd, buf); ret=-1; quit++; } if(buf) { free(buf); buf=NULL; } } return ret; }
static DWORD WINAPI read_efs(PBYTE pbData, PVOID pvCallbackContext, PULONG ulLength) { char cmd='\0'; size_t len=0; char *buf=NULL; struct winbuf *mybuf=(struct winbuf *)pvCallbackContext; while(1) { if(async_read(&cmd, &buf, &len)) return ERROR_FUNCTION_FAILED; (*(mybuf->rcvd))+=len; switch(cmd) { case CMD_APPEND: memcpy(pbData, buf, len); *ulLength=(ULONG)len; (*(mybuf->sent))+=len; free(buf); return ERROR_SUCCESS; case CMD_END_FILE: *ulLength=0; free(buf); return ERROR_SUCCESS; case CMD_WARNING: logp("WARNING: %s\n", buf); do_filecounter(mybuf->cntr, cmd, 0); free(buf); continue; default: logp("unknown append cmd: %c\n", cmd); free(buf); break; } } return ERROR_FUNCTION_FAILED; }
/* Read from fp if given - else read from our fd */ int async_read_stat(FILE *fp, gzFile zp, struct sbuf *sb, struct cntr *cntr) { size_t len=0; char *buf=NULL; char cmd=CMD_ERROR; char *d=NULL; while(1) { if(fp || zp) { int asr; if((asr=async_read_fp(fp, zp, &cmd, &buf, &len))) { //logp("async_read_fp returned: %d\n", asr); if(d) free(d); return asr; } if(buf[len]=='\n') buf[len]='\0'; } else { if(async_read(&cmd, &buf, &len)) { break; } if(cmd==CMD_WARNING) { logp("WARNING: %s\n", buf); do_filecounter(cntr, cmd, 0); if(buf) { free(buf); buf=NULL; } continue; } } if(cmd==CMD_DATAPTH) { if(d) free(d); d=buf; buf=NULL; } else if(cmd==CMD_STAT) { decode_stat(buf, &(sb->statp), &(sb->winattr), &(sb->compression)); sb->statbuf=buf; sb->slen=len; sb->datapth=d; return 0; } else if((cmd==CMD_GEN && !strcmp(buf, "backupend")) || (cmd==CMD_GEN && !strcmp(buf, "restoreend")) || (cmd==CMD_GEN && !strcmp(buf, "phase1end")) || (cmd==CMD_GEN && !strcmp(buf, "backupphase2")) || (cmd==CMD_GEN && !strcmp(buf, "estimateend"))) { if(d) free(d); return 1; } else { logp("expected cmd %c or %c, got '%c'\n", CMD_DATAPTH, CMD_STAT, cmd); if(buf) { free(buf); buf=NULL; } break; } } if(d) free(d); return -1; }
int send_file(FF_PKT *ff, bool top_level, struct config *conf, struct cntr *p1cntr) { char msg[128]=""; char attribs[MAXSTRING]; if(!file_is_included(conf->incexcdir, conf->iecount, conf->incext, conf->incount, conf->excext, conf->excount, conf->increg, conf->ircount, conf->excreg, conf->ercount, ff->fname, top_level)) return 0; #ifdef HAVE_WIN32 // Useful Windows attributes debug /* printf("\n%llu", ff->winattr); printf("\n%s\n", ff->fname); if(ff->winattr & FILE_ATTRIBUTE_READONLY) printf("readonly\n"); if(ff->winattr & FILE_ATTRIBUTE_HIDDEN) printf("hidden\n"); if(ff->winattr & FILE_ATTRIBUTE_SYSTEM) printf("system\n"); if(ff->winattr & FILE_ATTRIBUTE_DIRECTORY) printf("directory\n"); if(ff->winattr & FILE_ATTRIBUTE_ARCHIVE) printf("archive\n"); if(ff->winattr & FILE_ATTRIBUTE_DEVICE) printf("device\n"); if(ff->winattr & FILE_ATTRIBUTE_NORMAL) printf("normal\n"); if(ff->winattr & FILE_ATTRIBUTE_TEMPORARY) printf("temporary\n"); if(ff->winattr & FILE_ATTRIBUTE_SPARSE_FILE) printf("sparse\n"); if(ff->winattr & FILE_ATTRIBUTE_REPARSE_POINT) printf("reparse\n"); if(ff->winattr & FILE_ATTRIBUTE_COMPRESSED) printf("compressed\n"); if(ff->winattr & FILE_ATTRIBUTE_OFFLINE) printf("offline\n"); if(ff->winattr & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) printf("notcont\n"); if(ff->winattr & FILE_ATTRIBUTE_ENCRYPTED) printf("encrypted\n"); if(ff->winattr & FILE_ATTRIBUTE_VIRTUAL) printf("virtual\n"); */ if(ff->winattr & FILE_ATTRIBUTE_ENCRYPTED) { // if(ff->type!=FT_DIREND) // logw(p1cntr, "EFS not yet supported: %s", ff->fname); // return 0; if(ff->type==FT_REGE || ff->type==FT_REG || ff->type==FT_DIRBEGIN) { encode_stat(attribs, &ff->statp, ff->winattr, conf->compression); if(async_write_str(CMD_STAT, attribs) || async_write_str(CMD_EFS_FILE, ff->fname)) return -1; do_filecounter(p1cntr, CMD_EFS_FILE, 1); if(ff->type==FT_REG) do_filecounter_bytes(p1cntr, (unsigned long long)ff->statp.st_size); return 0; } else if(ff->type==FT_DIREND) return 0; else { // Hopefully, here is never reached. logw(p1cntr, "EFS type %d not yet supported: %s", ff->type, ff->fname); return 0; } } #endif //logp("%d: %s\n", ff->type, ff->fname); switch (ff->type) { case FT_LNKSAVED: //printf("Lnka: %s -> %s\n", ff->fname, ff->link); encode_stat(attribs, &ff->statp, ff->winattr, conf->compression); if(async_write_str(CMD_STAT, attribs) || async_write_str(CMD_HARD_LINK, ff->fname) || async_write_str(CMD_HARD_LINK, ff->link)) return -1; do_filecounter(p1cntr, CMD_HARD_LINK, 1); // At least FreeBSD 8.2 can have different xattrs on hard links. if(maybe_send_extrameta(ff->fname, CMD_HARD_LINK, attribs, p1cntr)) return -1; break; case FT_FIFO: case FT_REGE: case FT_REG: encode_stat(attribs, &ff->statp, ff->winattr, in_exclude_comp(conf->excom, conf->excmcount, ff->fname, conf->compression)); if(async_write_str(CMD_STAT, attribs) || async_write_str(filesymbol, ff->fname)) return -1; do_filecounter(p1cntr, filesymbol, 1); if(ff->type==FT_REG) do_filecounter_bytes(p1cntr, (unsigned long long)ff->statp.st_size); if(maybe_send_extrameta(ff->fname, filesymbol, attribs, p1cntr)) return -1; break; case FT_LNK: //printf("link: %s -> %s\n", ff->fname, ff->link); encode_stat(attribs, &ff->statp, ff->winattr, conf->compression); if(async_write_str(CMD_STAT, attribs) || async_write_str(CMD_SOFT_LINK, ff->fname) || async_write_str(CMD_SOFT_LINK, ff->link)) return -1; do_filecounter(p1cntr, CMD_SOFT_LINK, 1); if(maybe_send_extrameta(ff->fname, CMD_SOFT_LINK, attribs, p1cntr)) return -1; break; case FT_DIREND: return 0; case FT_NOFSCHG: case FT_DIRBEGIN: case FT_REPARSE: case FT_JUNCTION: { char errmsg[100] = ""; if (ff->type == FT_NOFSCHG) snprintf(errmsg, sizeof(errmsg), _("\t[will not descend: file system change not allowed]")); if(*errmsg) { snprintf(msg, sizeof(msg), "%s%s%s\n", "Dir: ", ff->fname, errmsg); logw(p1cntr, "%s", msg); } else { encode_stat(attribs, &ff->statp, ff->winattr, conf->compression); if(async_write_str(CMD_STAT, attribs)) return -1; #if defined(WIN32_VSS) if(async_write_str(filesymbol, ff->fname)) return -1; do_filecounter(p1cntr, filesymbol, 1); #else if(async_write_str(CMD_DIRECTORY, ff->fname)) return -1; do_filecounter(p1cntr, CMD_DIRECTORY, 1); if(maybe_send_extrameta(ff->fname, CMD_DIRECTORY, attribs, p1cntr)) return -1; #endif } } break; case FT_SPEC: // special file - fifo, socket, device node... encode_stat(attribs, &ff->statp, ff->winattr, conf->compression); if(async_write_str(CMD_STAT, attribs) || async_write_str(CMD_SPECIAL, ff->fname)) return -1; do_filecounter(p1cntr, CMD_SPECIAL, 1); if(maybe_send_extrameta(ff->fname, CMD_SPECIAL, attribs, p1cntr)) return -1; break; case FT_NOACCESS: logw(p1cntr, _("Err: Could not access %s: %s"), ff->fname, strerror(errno)); break; case FT_NOFOLLOW: logw(p1cntr, _("Err: Could not follow ff->link %s: %s"), ff->fname, strerror(errno)); break; case FT_NOSTAT: logw(p1cntr, _("Err: Could not stat %s: %s"), ff->fname, strerror(errno)); break; case FT_NOCHG: logw(p1cntr, _("Skip: File not saved. No change. %s"), ff->fname); break; case FT_ISARCH: logw(p1cntr, _("Err: Attempt to backup archive. Not saved. %s"), ff->fname); break; case FT_NOOPEN: logw(p1cntr, _("Err: Could not open directory %s: %s"), ff->fname, strerror(errno)); break; case FT_RAW: logw(p1cntr, _("Err: Raw partition: %s"), ff->fname); break; default: logw(p1cntr, _("Err: Unknown file ff->type %d: %s"), ff->type, ff->fname); break; } return 0; }
static int do_backup_phase2_client(struct config *conf, int resume, struct cntr *cntr) { int ret=0; int quit=0; char cmd; char *buf=NULL; size_t len=0; char attribs[MAXSTRING]; struct sbuf sb; init_sbuf(&sb); if(!resume) { // Only do this bit if the server did not tell us to resume. if(async_write_str(CMD_GEN, "backupphase2") || async_read_expect(CMD_GEN, "ok")) return -1; } while(!quit) { if(async_read(&cmd, &buf, &len)) { ret=-1; quit++; } else if(buf) { //logp("now: %c:%s\n", cmd, buf); if(cmd==CMD_DATAPTH) { sb.datapth=buf; buf=NULL; continue; } else if(cmd==CMD_STAT) { // Ignore the stat data - we will fill it // in again. Some time may have passed by now, // and it is best to make it as fresh as // possible. free(buf); buf=NULL; continue; } else if(cmd==CMD_FILE || cmd==CMD_ENC_FILE || cmd==CMD_METADATA || cmd==CMD_ENC_METADATA || cmd==CMD_EFS_FILE) { int forget=0; int64_t winattr=0; struct stat statbuf; char *extrameta=NULL; size_t elen=0; unsigned long long bytes=0; BFILE bfd; FILE *fp=NULL; sb.path=buf; buf=NULL; #ifdef HAVE_WIN32 if(win32_lstat(sb.path, &statbuf, &winattr)) #else if(lstat(sb.path, &statbuf)) #endif { logw(cntr, "Path has vanished: %s", sb.path); if(forget_file(&sb, cmd, cntr)) { ret=-1; quit++; } free_sbuf(&sb); continue; } if(conf->min_file_size && statbuf.st_size<(boffset_t)conf->min_file_size) { logw(cntr, "File size decreased below min_file_size after initial scan: %s", sb.path); forget++; } else if(conf->max_file_size && statbuf.st_size>(boffset_t)conf->max_file_size) { logw(cntr, "File size increased above max_file_size after initial scan: %s", sb.path); forget++; } if(!forget) { encode_stat(attribs, &statbuf, winattr); if(open_file_for_send(&bfd, &fp, sb.path, winattr, cntr)) forget++; } if(forget) { if(forget_file(&sb, cmd, cntr)) { ret=-1; quit++; } free_sbuf(&sb); continue; } if(cmd==CMD_METADATA || cmd==CMD_ENC_METADATA) { if(get_extrameta(sb.path, &statbuf, &extrameta, &elen, cntr)) { logw(cntr, "Meta data error for %s", sb.path); free_sbuf(&sb); close_file_for_send(&bfd, &fp); continue; } if(!extrameta) { logw(cntr, "No meta data after all: %s", sb.path); free_sbuf(&sb); close_file_for_send(&bfd, &fp); continue; } } if(cmd==CMD_FILE && sb.datapth) { unsigned long long sentbytes=0; // Need to do sig/delta stuff. if(async_write_str(CMD_DATAPTH, sb.datapth) || async_write_str(CMD_STAT, attribs) || async_write_str(CMD_FILE, sb.path) || load_signature_and_send_delta( &bfd, fp, &bytes, &sentbytes, cntr)) { logp("error in sig/delta for %s (%s)\n", sb.path, sb.datapth); ret=-1; quit++; } else { do_filecounter(cntr, CMD_FILE_CHANGED, 1); do_filecounter_bytes(cntr, bytes); do_filecounter_sentbytes(cntr, sentbytes); } } else { //logp("need to send whole file: %s\n", // sb.path); // send the whole file. if(async_write_str(CMD_STAT, attribs) || async_write_str(cmd, sb.path) || send_whole_file_w(cmd, sb.path, NULL, 0, &bytes, conf->encryption_password, cntr, conf->compression, &bfd, fp, extrameta, elen)) { ret=-1; quit++; } else { do_filecounter(cntr, cmd, 1); do_filecounter_bytes(cntr, bytes); do_filecounter_sentbytes(cntr, bytes); } } close_file_for_send(&bfd, &fp); free_sbuf(&sb); if(extrameta) free(extrameta); } else if(cmd==CMD_WARNING) { do_filecounter(cntr, cmd, 0); free(buf); buf=NULL; } else if(cmd==CMD_GEN && !strcmp(buf, "backupphase2end")) { if(async_write_str(CMD_GEN, "okbackupphase2end")) ret=-1; quit++; } else { logp("unexpected cmd from server: %c %s\n", cmd, buf); ret=-1; quit++; free(buf); buf=NULL; } } } return ret; }
// a = length of struct bu array // i = position to restore from static int restore_manifest(struct bu *arr, int a, int i, const char *tmppath1, const char *tmppath2, regex_t *regex, int srestore, enum action act, const char *client, char **dir_for_notify, struct cntr *p1cntr, struct cntr *cntr, struct config *cconf) { int ret=0; gzFile zp=NULL; char *manifest=NULL; char *datadir=NULL; FILE *logfp=NULL; char *logpath=NULL; char *logpathz=NULL; // For sending status information up to the server. char status=STATUS_RESTORING; if(act==ACTION_RESTORE) status=STATUS_RESTORING; else if(act==ACTION_VERIFY) status=STATUS_VERIFYING; if( (act==ACTION_RESTORE && !(logpath=prepend_s(arr[i].path, "restorelog", strlen("restorelog")))) || (act==ACTION_RESTORE && !(logpathz=prepend_s(arr[i].path, "restorelog.gz", strlen("restorelog.gz")))) || (act==ACTION_VERIFY && !(logpath=prepend_s(arr[i].path, "verifylog", strlen("verifylog")))) || (act==ACTION_VERIFY && !(logpathz=prepend_s(arr[i].path, "verifylog.gz", strlen("verifylog.gz")))) || !(manifest=prepend_s(arr[i].path, "manifest.gz", strlen("manifest.gz")))) { log_and_send_oom(__FUNCTION__); ret=-1; } else if(!(logfp=open_file(logpath, "ab")) || set_logfp(logfp, cconf)) { char msg[256]=""; snprintf(msg, sizeof(msg), "could not open log file: %s", logpath); log_and_send(msg); ret=-1; } *dir_for_notify=strdup(arr[i].path); log_restore_settings(cconf, srestore); // First, do a pass through the manifest to set up the counters. // This is the equivalent of a phase1 scan during backup. if(!ret && !(zp=gzopen_file(manifest, "rb"))) { log_and_send("could not open manifest"); ret=-1; } else { int ars=0; int quit=0; struct sbuf sb; init_sbuf(&sb); while(!quit) { if((ars=sbuf_fill(NULL, zp, &sb, cntr))) { if(ars<0) ret=-1; // ars==1 means end ok quit++; } else { if((!srestore || check_srestore(cconf, sb.path)) && check_regex(regex, sb.path)) { do_filecounter(p1cntr, sb.cmd, 0); if(sb.endfile) do_filecounter_bytes(p1cntr, strtoull(sb.endfile, NULL, 10)); /* if(sb.cmd==CMD_FILE || sb.cmd==CMD_ENC_FILE || sb.cmd==CMD_METADATA || sb.cmd==CMD_ENC_METADATA || sb.cmd==CMD_VSS || sb.cmd==CMD_ENC_VSS || sb.cmd==CMD_VSS_T || sb.cmd==CMD_ENC_VSS_T || sb.cmd==CMD_EFS_FILE) do_filecounter_bytes(p1cntr, (unsigned long long) sb.statp.st_size); */ } } free_sbuf(&sb); } free_sbuf(&sb); gzclose_fp(&zp); } if(cconf->send_client_counters) { if(send_counters(client, p1cntr, cntr)) { ret=-1; } } // Now, do the actual restore. if(!ret && !(zp=gzopen_file(manifest, "rb"))) { log_and_send("could not open manifest"); ret=-1; } else { char cmd; int s=0; int quit=0; size_t len=0; struct sbuf sb; // For out-of-sequence directory restoring so that the // timestamps come out right: int scount=0; struct sbuf **sblist=NULL; init_sbuf(&sb); while(!quit) { int ars=0; char *buf=NULL; if(async_read_quick(&cmd, &buf, &len)) { logp("read quick error\n"); ret=-1; quit++; break; } if(buf) { //logp("got read quick\n"); if(cmd==CMD_WARNING) { logp("WARNING: %s\n", buf); do_filecounter(cntr, cmd, 0); free(buf); buf=NULL; continue; } else if(cmd==CMD_INTERRUPT) { // Client wanted to interrupt the // sending of a file. But if we are // here, we have already moved on. // Ignore. free(buf); buf=NULL; continue; } else { logp("unexpected cmd from client: %c:%s\n", cmd, buf); free(buf); buf=NULL; ret=-1; quit++; break; } } if((ars=sbuf_fill(NULL, zp, &sb, cntr))) { if(ars<0) ret=-1; // ars==1 means end ok quit++; } else { if((!srestore || check_srestore(cconf, sb.path)) && check_regex(regex, sb.path) && restore_ent(client, &sb, &sblist, &scount, arr, a, i, tmppath1, tmppath2, act, status, cconf, cntr, p1cntr)) { ret=-1; quit++; } } free_sbuf(&sb); } gzclose_fp(&zp); // Restore any directories that are left in the list. if(!ret) for(s=scount-1; s>=0; s--) { if(restore_sbuf(sblist[s], arr, a, i, tmppath1, tmppath2, act, client, status, p1cntr, cntr, cconf)) { ret=-1; break; } } free_sbufs(sblist, scount); if(!ret) ret=do_restore_end(act, cntr); //print_endcounter(cntr); print_filecounters(p1cntr, cntr, act); reset_filecounter(p1cntr, time(NULL)); reset_filecounter(cntr, time(NULL)); } set_logfp(NULL, cconf); compress_file(logpath, logpathz, cconf); if(manifest) free(manifest); if(datadir) free(datadir); if(logpath) free(logpath); if(logpathz) free(logpathz); return ret; }
// returns 1 for finished ok. static int do_stuff_to_receive(struct sbuf *rb, FILE *p2fp, const char *datadirtmp, struct dpth *dpth, const char *working, char **last_requested, const char *deltmppath, struct cntr *cntr, struct config *cconf) { int ret=0; char rcmd; size_t rlen=0; size_t wlen=0; char *rbuf=NULL; // This also attempts to write anything in the write buffer. if(async_rw(&rcmd, &rbuf, &rlen, '\0', NULL, &wlen)) { logp("error in async_rw\n"); return -1; } if(rbuf) { if(rcmd==CMD_WARNING) { logp("WARNING: %s\n", rbuf); do_filecounter(cntr, rcmd, 0); } else if(rb->fp || rb->zp) { // Currently writing a file (or meta data) if(rcmd==CMD_APPEND) { int app; //logp("rlen: %d\n", rlen); if((rb->zp && (app=gzwrite(rb->zp, rbuf, rlen))<=0) || (rb->fp && (app=fwrite(rbuf, 1, rlen, rb->fp))<=0)) { logp("error when appending: %d\n", app); async_write_str(CMD_ERROR, "write failed"); ret=-1; } do_filecounter_recvbytes(cntr, rlen); } else if(rcmd==CMD_END_FILE) { // Finished the file. // Write it to the phase2 file, and free the // buffers. if(close_fp(&(rb->fp))) { logp("error closing delta for %s in receive\n", rb->path); ret=-1; } if(gzclose_fp(&(rb->zp))) { logp("error gzclosing delta for %s in receive\n", rb->path); ret=-1; } rb->endfile=rbuf; rb->elen=rlen; rbuf=NULL; if(!ret && rb->receivedelta && finish_delta(rb, working, deltmppath)) ret=-1; else if(!ret) { if(sbuf_to_manifest(rb, p2fp, NULL)) ret=-1; else { char cmd=rb->cmd; if(rb->receivedelta) do_filecounter_changed(cntr, cmd); else do_filecounter(cntr, cmd, 0); if(*last_requested && !strcmp(rb->path, *last_requested)) { free(*last_requested); *last_requested=NULL; } } } if(!ret) { char *cp=NULL; cp=strchr(rb->endfile, ':'); if(rb->endfile) do_filecounter_bytes(cntr, strtoull(rb->endfile, NULL, 10)); if(cp) { // checksum stuff goes here } } free_sbuf(rb); } else { logp("unexpected cmd from client while writing file: %c %s\n", rcmd, rbuf); ret=-1; } } // Otherwise, expecting to be told of a file to save. else if(rcmd==CMD_DATAPTH) { rb->datapth=rbuf; rbuf=NULL; } else if(rcmd==CMD_STAT) { rb->statbuf=rbuf; rb->slen=rlen; rbuf=NULL; } else if(filedata(rcmd)) { rb->cmd=rcmd; rb->plen=rlen; rb->path=rbuf; rbuf=NULL; if(rb->datapth) { // Receiving a delta. if(start_to_receive_delta(rb, working, deltmppath, cconf)) { logp("error in start_to_receive_delta\n"); ret=-1; } } else { // Receiving a whole new file. if(start_to_receive_new_file(rb, datadirtmp, dpth, cntr, cconf)) { logp("error in start_to_receive_new_file\n"); ret=-1; } } } else if(rcmd==CMD_GEN && !strcmp(rbuf, "okbackupphase2end")) { ret=1; //logp("got okbackupphase2end\n"); } else if(rcmd==CMD_INTERRUPT) { // Interrupt - forget about the last requested file // if it matches. Otherwise, we can get stuck on the // select in the async stuff, waiting for something // that will never arrive. if(*last_requested && !strcmp(rbuf, *last_requested)) { free(*last_requested); *last_requested=NULL; } } else { logp("unexpected cmd from client while expecting a file: %c %s\n", rcmd, rbuf); ret=-1; } if(rbuf) { free(rbuf); rbuf=NULL; } } //logp("returning: %d\n", ret); return ret; }
/* * If the stream has no more data available, read some from F into * BUF, and let the stream use that. On return, SEEN_EOF is true if * the end of file has passed into the stream. */ rs_result rs_infilebuf_fill(rs_job_t *job, rs_buffers_t *buf, void *opaque) { int len=0; rs_filebuf_t *fb = (rs_filebuf_t *) opaque; gzFile zp = fb->zp; FILE *fp = fb->fp; struct cntr *cntr; int fd=fb->fd; cntr=fb->cntr; //logp("rs_infilebuf_fill\n"); /* This is only allowed if either the buf has no input buffer * yet, or that buffer could possibly be BUF. */ if (buf->next_in != NULL) { //logp("infilebuf avail_in %d buf_len %d\n", buf->avail_in, fb->buf_len); assert(buf->avail_in <= fb->buf_len); assert(buf->next_in >= fb->buf); assert(buf->next_in <= fb->buf + fb->buf_len); } else { assert(buf->avail_in == 0); } if (buf->eof_in) { return RS_DONE; } if (buf->avail_in) /* Still some data remaining. Perhaps we should read anyhow? */ return RS_DONE; if(fd>=0) { char rcmd; size_t rlen; char *rbuf=NULL; if(async_read(&rcmd, &rbuf, &rlen)) return RS_IO_ERROR; if(rcmd==CMD_APPEND) { //logp("got '%c' in fd infilebuf: %d\n", CMD_APPEND, rlen); memcpy(fb->buf, rbuf, rlen); len=rlen; free(rbuf); } else if(rcmd==CMD_END_FILE) { free(rbuf); //logp("got %c in fd infilebuf\n", CMD_END_FILE); buf->eof_in=1; return RS_DONE; } else if(rcmd==CMD_WARNING) { logp("WARNING: %s\n", rbuf); do_filecounter(cntr, rcmd, 0); free(rbuf); return RS_DONE; } else { logp("unexpected cmd in rs_infilebuf_fill: %c:%s\n", rcmd, rbuf); free(rbuf); return RS_IO_ERROR; } } #ifdef HAVE_WIN32 else if(fb->bfd) { if(fb->do_known_byte_count) { len=bread(fb->bfd, fb->buf, min(fb->buf_len, fb->data_len)); fb->data_len-=len; } else len=bread(fb->bfd, fb->buf, fb->buf_len); if(len==0) { //logp("bread: eof\n"); buf->eof_in=1; return RS_DONE; } else if(len<0) { logp("rs_infilebuf_fill: error in bread\n"); return RS_IO_ERROR; } //logp("bread: ok: %d\n", len); fb->bytes+=len; if(!MD5_Update(&(fb->md5), fb->buf, len)) { logp("rs_infilebuf_fill: MD5_Update() failed\n"); return RS_IO_ERROR; } // Windows VSS headers have given us the data length to expect. if(fb->do_known_byte_count && fb->data_len<=0) { buf->eof_in=1; return RS_DONE; } } #endif else if(fp) { len = fread(fb->buf, 1, fb->buf_len, fp); //logp("fread: %d\n", len); if (len <= 0) { /* This will happen if file size is a multiple of input block len */ if (feof(fp)) { buf->eof_in=1; return RS_DONE; } else { logp("rs_infilebuf_fill: got return %d when trying to read\n", len); return RS_IO_ERROR; } } fb->bytes+=len; if(!MD5_Update(&(fb->md5), fb->buf, len)) { logp("rs_infilebuf_fill: MD5_Update() failed\n"); return RS_IO_ERROR; } } else if(zp) { len = gzread(zp, fb->buf, fb->buf_len); //logp("gzread: %d\n", len); if (len <= 0) { /* This will happen if file size is a multiple of input block len */ if (gzeof(zp)) { buf->eof_in=1; return RS_DONE; } else { logp("rs_infilebuf_fill: got return %d when trying to read\n", len); return RS_IO_ERROR; } } fb->bytes+=len; /* This bit cannot ever have been working right, because gzeof(fp) probably always returns 0. if (len < (int)fb->buf_len && gzeof(fp)) { buf->eof_in=1; return RS_DONE; } */ if(!MD5_Update(&(fb->md5), fb->buf, len)) { logp("rs_infilebuf_fill: MD5_Update() failed\n"); return RS_IO_ERROR; } } buf->avail_in = len; buf->next_in = fb->buf; return RS_DONE; }
// a = length of struct bu array // i = position to restore from static int restore_manifest(struct bu *arr, int a, int i, const char *tmppath1, const char *tmppath2, regex_t *regex, enum action act, const char *client, struct cntr *p1cntr, struct cntr *cntr, struct config *cconf, bool all) { int ret=0; gzFile zp=NULL; char *manifest=NULL; char *datadir=NULL; FILE *logfp=NULL; char *logpath=NULL; char *logpathz=NULL; // For sending status information up to the server. char status=STATUS_RESTORING; if(act==ACTION_RESTORE) status=STATUS_RESTORING; else if(act==ACTION_VERIFY) status=STATUS_VERIFYING; if( (act==ACTION_RESTORE && !(logpath=prepend_s(arr[i].path, "restorelog", strlen("restorelog")))) || (act==ACTION_RESTORE && !(logpathz=prepend_s(arr[i].path, "restorelog.gz", strlen("restorelog.gz")))) || (act==ACTION_VERIFY && !(logpath=prepend_s(arr[i].path, "verifylog", strlen("verifylog")))) || (act==ACTION_VERIFY && !(logpathz=prepend_s(arr[i].path, "verifylog.gz", strlen("verifylog.gz")))) || !(manifest=prepend_s(arr[i].path, "manifest.gz", strlen("manifest.gz")))) { log_and_send("out of memory"); ret=-1; } else if(!(logfp=open_file(logpath, "ab")) || set_logfp(logfp)) { char msg[256]=""; snprintf(msg, sizeof(msg), "could not open log file: %s", logpath); log_and_send(msg); ret=-1; } else if(!(zp=gzopen_file(manifest, "rb"))) { log_and_send("could not open manifest"); ret=-1; } else { char cmd; int quit=0; size_t len=0; struct sbuf sb; // For out-of-sequence directory restoring so that the // timestamps come out right: int s=0; int scount=0; struct sbuf **sblist=NULL; init_sbuf(&sb); while(!quit) { int ars=0; char *buf=NULL; if(async_read_quick(&cmd, &buf, &len)) { logp("read quick error\n"); ret=-1; quit++; break; } if(buf) { //logp("got read quick\n"); if(cmd==CMD_WARNING) { logp("WARNING: %s\n", buf); do_filecounter(cntr, cmd, 0); free(buf); buf=NULL; continue; } else if(cmd==CMD_INTERRUPT) { // Client wanted to interrupt the // sending of a file. But if we are // here, we have already moved on. // Ignore. free(buf); buf=NULL; continue; } else { logp("unexpected cmd from client: %c:%s\n", cmd, buf); free(buf); buf=NULL; ret=-1; quit++; break; } } if((ars=sbuf_fill(NULL, zp, &sb, cntr))) { if(ars<0) ret=-1; // ars==1 means end ok quit++; } else { if(check_regex(regex, sb.path)) { // Check if we have any directories waiting // to be restored. for(s=scount-1; s>=0; s--) { if(is_subdir(sblist[s]->path, sb.path)) { // We are still in a subdir. //printf(" subdir (%s %s)\n", sblist[s]->path, sb.path); break; } else { // Can now restore sblist[s] // because nothing else is // fiddling in a subdirectory. if(restore_sbuf(sblist[s], arr, a, i, tmppath1, tmppath2, act, client, status, p1cntr, cntr, cconf)) { ret=-1; quit++; break; } else if(del_from_sbuf_arr( &sblist, &scount)) { ret=-1; quit++; break; } } } /* If it is a directory, need to remember it and restore it later, so that the permissions come out right. */ /* Meta data of directories will also have the stat stuff set to be a directory, so will also come out at the end. */ if(!ret && S_ISDIR(sb.statp.st_mode)) { if(add_to_sbuf_arr(&sblist, &sb, &scount)) { ret=-1; quit++; } // Wipe out sb, without freeing up // all the strings inside it, which // have been added to sblist. init_sbuf(&sb); } else if(!ret && restore_sbuf(&sb, arr, a, i, tmppath1, tmppath2, act, client, status, p1cntr, cntr, cconf)) { ret=-1; quit++; } } } free_sbuf(&sb); } gzclose_fp(&zp); // Restore any directories that are left in the list. if(!ret) for(s=scount-1; s>=0; s--) { if(restore_sbuf(sblist[s], arr, a, i, tmppath1, tmppath2, act, client, status, p1cntr, cntr, cconf)) { ret=-1; break; } } free_sbufs(sblist, scount); if(!ret && !all) ret=do_restore_end(act, cntr); print_endcounter(cntr); print_filecounters(p1cntr, cntr, act, 0); reset_filecounter(p1cntr); reset_filecounter(cntr); } set_logfp(NULL); compress_file(logpath, logpathz, cconf); if(manifest) free(manifest); if(datadir) free(datadir); if(logpath) free(logpath); if(logpathz) free(logpathz); return ret; }
int transfer_gzfile_in(struct sbuf *sb, const char *path, BFILE *bfd, FILE *fp, unsigned long long *rcvd, unsigned long long *sent, const char *encpassword, int enccompressed, struct cntr *cntr, char **metadata) { char cmd; char *buf=NULL; size_t len=0; int quit=0; int ret=-1; unsigned char out[ZCHUNK]; size_t doutlen=0; //unsigned char doutbuf[1000+EVP_MAX_BLOCK_LENGTH]; unsigned char doutbuf[ZCHUNK-EVP_MAX_BLOCK_LENGTH]; z_stream zstrm; EVP_CIPHER_CTX *enc_ctx=NULL; // Checksum stuff //MD5_CTX md5; //unsigned char checksum[MD5_DIGEST_LENGTH+1]; //logp("in transfer_gzfile_in\n"); #ifdef HAVE_WIN32 if(sb && sb->cmd==CMD_EFS_FILE) return transfer_efs_in(bfd, rcvd, sent, cntr); #endif //if(!MD5_Init(&md5)) //{ // logp("MD5_Init() failed"); // return -1; //} zstrm.zalloc=Z_NULL; zstrm.zfree=Z_NULL; zstrm.opaque=Z_NULL; zstrm.avail_in=0; zstrm.next_in=Z_NULL; if(inflateInit2(&zstrm, (15+16))) { logp("unable to init inflate\n"); return -1; } if(encpassword && !(enc_ctx=enc_setup(0, encpassword))) { inflateEnd(&zstrm); return -1; } while(!quit) { if(async_read(&cmd, &buf, &len)) { if(enc_ctx) { EVP_CIPHER_CTX_cleanup(enc_ctx); free(enc_ctx); } inflateEnd(&zstrm); return -1; } (*rcvd)+=len; //logp("transfer in: %c:%s\n", cmd, buf); switch(cmd) { case CMD_APPEND: // append if(!fp && !bfd && !metadata) { logp("given append, but no file or metadata to write to\n"); async_write_str(CMD_ERROR, "append with no file or metadata"); quit++; ret=-1; } else { size_t lentouse; unsigned char *buftouse=NULL; /* if(!MD5_Update(&md5, buf, len)) { logp("MD5 update enc error\n"); quit++; ret=-1; break; } */ // If doing decryption, it needs // to be done before uncompressing. if(enc_ctx) { // updating our checksum needs to // be done first /* if(!MD5_Update(&md5, buf, len)) { logp("MD5 update enc error\n"); quit++; ret=-1; break; } else */ if(!EVP_CipherUpdate(enc_ctx, doutbuf, (int *)&doutlen, (unsigned char *)buf, len)) { logp("Decryption error\n"); quit++; ret=-1; break; } if(!doutlen) break; lentouse=doutlen; buftouse=doutbuf; } else { lentouse=len; buftouse=(unsigned char *)buf; } //logp("want to write: %d\n", zstrm.avail_in); if(do_inflate(&zstrm, bfd, fp, out, buftouse, lentouse, metadata, encpassword, enccompressed, sent)) { ret=-1; quit++; break; } } break; case CMD_END_FILE: // finish up if(enc_ctx) { if(!EVP_CipherFinal_ex(enc_ctx, doutbuf, (int *)&doutlen)) { logp("Decryption failure at the end.\n"); ret=-1; quit++; break; } if(doutlen && do_inflate(&zstrm, bfd, fp, out, doutbuf, doutlen, metadata, encpassword, enccompressed, sent)) { ret=-1; quit++; break; } } /* if(MD5_Final(checksum, &md5)) { char *oldsum=NULL; const char *newsum=NULL; if((oldsum=strchr(buf, ':'))) { oldsum++; newsum=get_checksum_str(checksum); // log if the checksum differed if(strcmp(newsum, oldsum)) logw(cntr, "md5sum for '%s' did not match! (%s!=%s)\n", path, newsum, oldsum); } } else { logp("MD5_Final() failed\n"); } */ quit++; ret=0; break; case CMD_WARNING: logp("WARNING: %s\n", buf); do_filecounter(cntr, cmd, 0); break; default: logp("unknown append cmd: %c\n", cmd); quit++; ret=-1; break; } if(buf) free(buf); buf=NULL; } inflateEnd(&zstrm); if(enc_ctx) { EVP_CIPHER_CTX_cleanup(enc_ctx); free(enc_ctx); } if(ret) logp("transfer file returning: %d\n", ret); return ret; }
static int jiggle(const char *datapth, const char *currentdata, const char *datadirtmp, const char *datadir, const char *deltabdir, const char *deltafdir, const char *sigpath, const char *endfile, const char *deletionsfile, FILE **delfp, struct sbuf *sb, int hardlinked, int compression, struct cntr *cntr, struct config *cconf) { int ret=0; struct stat statp; char *oldpath=NULL; char *newpath=NULL; char *finpath=NULL; char *deltafpath=NULL; if(!(oldpath=prepend_s(currentdata, datapth, strlen(datapth))) || !(newpath=prepend_s(datadirtmp, datapth, strlen(datapth))) || !(finpath=prepend_s(datadir, datapth, strlen(datapth))) || !(deltafpath=prepend_s(deltafdir, datapth, strlen(datapth)))) { log_out_of_memory(__FUNCTION__); ret=-1; goto cleanup; } else if(!lstat(finpath, &statp) && S_ISREG(statp.st_mode)) { // Looks like an interrupted jiggle // did this file already. static int donemsg=0; if(!lstat(deltafpath, &statp) && S_ISREG(statp.st_mode)) { logp("deleting unneeded forward delta: %s\n", deltafpath); unlink(deltafpath); } if(!donemsg) { logp("skipping already present file: %s\n", finpath); logp("to save log space, skips of other already present files will not be logged\n"); donemsg++; } } else if(mkpath(&finpath, datadir)) { logp("could not create path for: %s\n", finpath); ret=-1; goto cleanup; } else if(mkpath(&newpath, datadirtmp)) { logp("could not create path for: %s\n", newpath); ret=-1; goto cleanup; } else if(!lstat(deltafpath, &statp) && S_ISREG(statp.st_mode)) { int lrs; char *infpath=NULL; // Got a forward patch to do. // First, need to gunzip the old file, // otherwise the librsync patch will take // forever, because it will be doing seeks // all over the place, and gzseeks are slow. if(!(infpath=prepend_s(deltafdir, "inflate", strlen("inflate")))) { log_out_of_memory(__FUNCTION__); ret=-1; goto cleanup; } //logp("Fixing up: %s\n", datapth); if(inflate_or_link_oldfile(oldpath, infpath, compression, cconf)) { logp("error when inflating old file: %s\n", oldpath); ret=-1; free(infpath); goto cleanup; } if((lrs=do_patch(infpath, deltafpath, newpath, cconf->compression, compression /* from the manifest */, cntr, cconf))) { logp("WARNING: librsync error when patching %s: %d\n", oldpath, lrs); do_filecounter(cntr, CMD_WARNING, 1); // Try to carry on with the rest of the backup // regardless. //ret=-1; // Remove anything that got written. unlink(newpath); unlink(infpath); free(infpath); // First, note that we want to remove this entry from // the manifest. if(!*delfp && !(*delfp=open_file(deletionsfile, "ab"))) { // Could not mark this file as deleted. Fatal. ret=-1; goto cleanup; } if(sbuf_to_manifest(sb, *delfp, NULL)) ret=-1; if(fflush(*delfp)) { logp("error fflushing deletions file in jiggle: %s\n", strerror(errno)); ret=-1; } goto cleanup; } // Get rid of the inflated old file. unlink(infpath); free(infpath); // Need to generate a reverse diff, // unless we are keeping a hardlinked // archive. if(!hardlinked) { if(gen_rev_delta(sigpath, deltabdir, oldpath, newpath, datapth, endfile, compression, cntr, cconf)) { ret=-1; goto cleanup; } } // Power interruptions should be // recoverable. If it happens before // this point, the data jiggle for // this file has to be done again. // Once finpath is in place, no more // jiggle is required. // Use the fresh new file. if(do_rename(newpath, finpath)) { ret=-1; goto cleanup; } else { // Remove the forward delta, as it is // no longer needed. There is a // reverse diff and the finished // finished file is in place. //logp("Deleting delta.forward...\n"); unlink(deltafpath); // Remove the old file. If a power // cut happens just before this, // the old file will hang around // forever. // TODO: Put in something to // detect this. // ie, both a reverse delta and the // old file exist. if(!hardlinked) { //logp("Deleting oldpath...\n"); unlink(oldpath); } } } else if(!lstat(newpath, &statp) && S_ISREG(statp.st_mode)) { // Use the fresh new file. // This needs to happen after checking // for the forward delta, because the // patching stuff writes to newpath. //logp("Using newly received file\n"); if(do_rename(newpath, finpath)) { ret=-1; goto cleanup; } } else if(!lstat(oldpath, &statp) && S_ISREG(statp.st_mode)) { // Use the old unchanged file. // Hard link it first. //logp("Hard linking to old file: %s\n", datapth); if(do_link(oldpath, finpath, &statp, cconf, FALSE /* do not overwrite finpath (should never need to) */)) { ret=-1; goto cleanup; } else { // If we are not keeping a hardlinked // archive, delete the old link. if(!hardlinked) { //logp("Unlinking old file: %s\n", oldpath); unlink(oldpath); } } } else { logp("could not find: %s\n", oldpath); ret=-1; goto cleanup; } cleanup: if(oldpath) { free(oldpath); oldpath=NULL; } if(newpath) { free(newpath); newpath=NULL; } if(finpath) { free(finpath); finpath=NULL; } if(deltafpath) { free(deltafpath); deltafpath=NULL; } return ret; }
static int do_backup_phase2_client(struct config *conf, int resume, struct cntr *p1cntr, struct cntr *cntr) { int ret=0; int quit=0; char cmd; char *buf=NULL; size_t len=0; char attribs[MAXSTRING]; // For efficiency, open Windows files for the VSS data, and do not // close them until another time around the loop, when the actual // data is read. BFILE bfd; // Windows VSS headers tell us how much file // data to expect. size_t datalen=0; #ifdef HAVE_WIN32 binit(&bfd, 0); #endif struct sbuf sb; init_sbuf(&sb); if(!resume) { // Only do this bit if the server did not tell us to resume. if(async_write_str(CMD_GEN, "backupphase2") || async_read_expect(CMD_GEN, "ok")) return -1; } else if(conf->send_client_counters) { // On resume, the server might update the client with the // counters. if(recv_counters(p1cntr, cntr)) return -1; } while(!quit) { if(async_read(&cmd, &buf, &len)) { ret=-1; quit++; } else if(buf) { //logp("now: %c:%s\n", cmd, buf); if(cmd==CMD_DATAPTH) { sb.datapth=buf; buf=NULL; continue; } else if(cmd==CMD_STAT) { // Ignore the stat data - we will fill it // in again. Some time may have passed by now, // and it is best to make it as fresh as // possible. free(buf); buf=NULL; continue; } else if(cmd==CMD_FILE || cmd==CMD_ENC_FILE || cmd==CMD_METADATA || cmd==CMD_ENC_METADATA || cmd==CMD_VSS || cmd==CMD_ENC_VSS || cmd==CMD_VSS_T || cmd==CMD_ENC_VSS_T || cmd==CMD_EFS_FILE) { int forget=0; int64_t winattr=0; struct stat statbuf; char *extrameta=NULL; size_t elen=0; unsigned long long bytes=0; FILE *fp=NULL; int compression=conf->compression; sb.path=buf; buf=NULL; #ifdef HAVE_WIN32 if(win32_lstat(sb.path, &statbuf, &winattr)) #else if(lstat(sb.path, &statbuf)) #endif { logw(cntr, "Path has vanished: %s", sb.path); if(forget_file(&sb, cmd, cntr)) { ret=-1; quit++; } free_sbuf(&sb); continue; } if(conf->min_file_size && statbuf.st_size< (boffset_t)conf->min_file_size && (cmd==CMD_FILE || cmd==CMD_ENC_FILE || cmd==CMD_EFS_FILE)) { logw(cntr, "File size decreased below min_file_size after initial scan: %c:%s", cmd, sb.path); forget++; } else if(conf->max_file_size && statbuf.st_size> (boffset_t)conf->max_file_size && (cmd==CMD_FILE || cmd==CMD_ENC_FILE || cmd==CMD_EFS_FILE)) { logw(cntr, "File size increased above max_file_size after initial scan: %c:%s", cmd, sb.path); forget++; } if(!forget) { compression=in_exclude_comp(conf->excom, conf->excmcount, sb.path, conf->compression); encode_stat(attribs, &statbuf, winattr, compression); if(open_file_for_send( #ifdef HAVE_WIN32 &bfd, NULL, #else NULL, &fp, #endif sb.path, winattr, &datalen, cntr)) forget++; } if(forget) { if(forget_file(&sb, cmd, cntr)) { ret=-1; quit++; } free_sbuf(&sb); continue; } if(cmd==CMD_METADATA || cmd==CMD_ENC_METADATA || cmd==CMD_VSS || cmd==CMD_ENC_VSS #ifdef HAVE_WIN32 || conf->strip_vss #endif ) { if(get_extrameta( #ifdef HAVE_WIN32 &bfd, #else NULL, #endif sb.path, &statbuf, &extrameta, &elen, winattr, cntr, &datalen)) { logw(cntr, "Meta data error for %s", sb.path); free_sbuf(&sb); close_file_for_send(&bfd, &fp); continue; } if(extrameta) { #ifdef HAVE_WIN32 if(conf->strip_vss) { free(extrameta); extrameta=NULL; elen=0; } #endif } else { logw(cntr, "No meta data after all: %s", sb.path); free_sbuf(&sb); close_file_for_send(&bfd, &fp); continue; } } if(cmd==CMD_FILE && sb.datapth) { unsigned long long sentbytes=0; // Need to do sig/delta stuff. if(async_write_str(CMD_DATAPTH, sb.datapth) || async_write_str(CMD_STAT, attribs) || async_write_str(CMD_FILE, sb.path) || load_signature_and_send_delta( &bfd, fp, &bytes, &sentbytes, cntr, datalen)) { logp("error in sig/delta for %s (%s)\n", sb.path, sb.datapth); ret=-1; quit++; } else { do_filecounter(cntr, CMD_FILE_CHANGED, 1); do_filecounter_bytes(cntr, bytes); do_filecounter_sentbytes(cntr, sentbytes); } } else { //logp("need to send whole file: %s\n", // sb.path); // send the whole file. if((async_write_str(CMD_STAT, attribs) || async_write_str(cmd, sb.path)) || send_whole_file_w(cmd, sb.path, NULL, 0, &bytes, conf->encryption_password, cntr, compression, &bfd, fp, extrameta, elen, datalen)) { ret=-1; quit++; } else { do_filecounter(cntr, cmd, 1); do_filecounter_bytes(cntr, bytes); do_filecounter_sentbytes(cntr, bytes); } } #ifdef HAVE_WIN32 // If using Windows do not close bfd - it needs // to stay open to read VSS/file data/VSS. // It will get closed either when given a // different file path, or when this function // exits. //if(cmd!=CMD_VSS // && cmd!=CMD_ENC_VSS) // close_file_for_send(&bfd, NULL); #else close_file_for_send(NULL, &fp); #endif free_sbuf(&sb); if(extrameta) free(extrameta); } else if(cmd==CMD_WARNING) { do_filecounter(cntr, cmd, 0); free(buf); buf=NULL; } else if(cmd==CMD_GEN && !strcmp(buf, "backupphase2end")) { if(async_write_str(CMD_GEN, "okbackupphase2end")) ret=-1; quit++; } else { logp("unexpected cmd from server: %c %s\n", cmd, buf); ret=-1; quit++; free(buf); buf=NULL; } } } #ifdef HAVE_WIN32 // It is possible for a bfd to still be open. close_file_for_send(&bfd, NULL); #endif return ret; }
// a = length of struct bu array // i = position to restore from static int restore_file(struct bu *arr, int a, int i, const char *datapth, const char *fname, const char *tmppath1, const char *tmppath2, int act, const char *endfile, char cmd, int64_t winattr, int compression, struct cntr *cntr, struct config *cconf) { int x=0; char msg[256]=""; // Go up the array until we find the file in the data directory. for(x=i; x<a; x++) { char *path=NULL; struct stat statp; if(!(path=prepend_s(arr[x].data, datapth, strlen(datapth)))) { log_and_send_oom(__FUNCTION__); return -1; } //logp("server file: %s\n", path); if(lstat(path, &statp) || !S_ISREG(statp.st_mode)) { free(path); continue; } else { int patches=0; struct stat dstatp; const char *tmp=NULL; const char *best=NULL; unsigned long long bytes=0; best=path; tmp=tmppath1; // Now go down the array, applying any deltas. for(x-=1; x>=i; x--) { char *dpath=NULL; if(!(dpath=prepend_s(arr[x].delta, datapth, strlen(datapth)))) { log_and_send_oom(__FUNCTION__); free(path); return -1; } if(lstat(dpath, &dstatp) || !S_ISREG(dstatp.st_mode)) { free(dpath); continue; } if(!patches) { // Need to gunzip the first one. if(inflate_or_link_oldfile(best, tmp, compression)) { logp("error when inflating %s\n", best); free(path); free(dpath); return -1; } best=tmp; if(tmp==tmppath1) tmp=tmppath2; else tmp=tmppath1; } if(do_patch(best, dpath, tmp, FALSE /* do not gzip the result */, compression /* from the manifest */, cntr, cconf)) { char msg[256]=""; snprintf(msg, sizeof(msg), "error when patching %s\n", path); log_and_send(msg); free(path); free(dpath); return -1; } best=tmp; if(tmp==tmppath1) tmp=tmppath2; else tmp=tmppath1; unlink(tmp); patches++; } if(act==ACTION_RESTORE) { if(send_file(fname, patches, best, datapth, &bytes, cmd, winattr, compression, cntr, cconf)) { free(path); return -1; } else { do_filecounter(cntr, cmd, 0); do_filecounter_bytes(cntr, strtoull(endfile, NULL, 10)); } } else if(act==ACTION_VERIFY) { if(verify_file(fname, patches, best, datapth, &bytes, endfile, cmd, compression, cntr)) { free(path); return -1; } else { do_filecounter(cntr, cmd, 0); do_filecounter_bytes(cntr, strtoull(endfile, NULL, 10)); } } do_filecounter_sentbytes(cntr, bytes); free(path); return 0; } } logw(cntr, "restore could not find %s (%s)\n", fname, datapth); //return -1; return 0; }