// Return 0 for OK, -1 for error, 1 for timer conditions not met. static int do_backup_client(struct config *conf, int resume, int estimate, struct cntr *p1cntr, struct cntr *cntr) { int ret=0; if(estimate) logp("do estimate client\n"); else logp("do backup client\n"); #if defined(HAVE_WIN32) win32_enable_backup_privileges(); #endif #if defined(WIN32_VSS) if((ret=win32_start_vss(conf))) return ret; #endif // Scan the file system and send the results to the server. // Skip phase1 if the server wanted to resume. if(!ret && !resume) ret=backup_phase1_client(conf, estimate, p1cntr, cntr); // Now, the server will be telling us what data we need to send. if(!estimate && !ret) ret=backup_phase2_client(conf, p1cntr, resume, cntr); if(estimate) print_filecounters(p1cntr, cntr, ACTION_ESTIMATE); #if defined(WIN32_VSS) win32_stop_vss(); #endif return ret; }
int backup_phase2_client(struct config *conf, struct cntr *p1cntr, int resume, struct cntr *cntr) { int ret=0; logp("Phase 2 begin (send file data)\n"); ret=do_backup_phase2_client(conf, resume, p1cntr, cntr); print_endcounter(cntr); print_filecounters(p1cntr, cntr, ACTION_BACKUP); if(ret) logp("Error in phase 2\n"); logp("Phase 2 end (send file data)\n"); 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; }
// 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 backup_phase4_server(const char *basedir, const char *working, const char *current, const char *currentdata, const char *finishing, struct config *cconf, const char *client, struct cntr *p1cntr, struct cntr *cntr) { int ret=0; struct stat statp; char *manifest=NULL; char *datadir=NULL; char *datadirtmp=NULL; char *currentdup=NULL; char *currentduptmp=NULL; char *currentdupdata=NULL; char *forward=NULL; char *timestamp=NULL; char *fullrealcurrent=NULL; char *deleteme=NULL; char *logpath=NULL; char *hlinkedpath=NULL; int len=0; char realcurrent[256]=""; FILE *logfp=NULL; if((len=readlink(current, realcurrent, sizeof(realcurrent)-1))<0) len=0; realcurrent[len]='\0'; if(!(datadir=prepend_s(finishing, "data", strlen("data"))) || !(datadirtmp=prepend_s(finishing, "data.tmp", strlen("data.tmp"))) || !(manifest=prepend_s(finishing, "manifest.gz", strlen("manifest.gz"))) || !(currentdup=prepend_s(finishing, "currentdup", strlen("currentdup"))) || !(currentduptmp=prepend_s(finishing, "currentdup.tmp", strlen("currentdup.tmp"))) || !(currentdupdata=prepend_s(currentdup, "data", strlen("data"))) || !(forward=prepend_s(currentdup, "forward", strlen("forward"))) || !(timestamp=prepend_s(finishing, "timestamp", strlen("timestamp"))) || !(fullrealcurrent=prepend_s(basedir, realcurrent, strlen(realcurrent))) || !(deleteme=prepend_s(basedir, "deleteme", strlen("deleteme"))) || !(logpath=prepend_s(finishing, "log", strlen("log"))) || !(hlinkedpath=prepend_s(currentdup, "hardlinked", strlen("hardlinked")))) { ret=-1; goto endfunc; } if(!(logfp=open_file(logpath, "ab")) || set_logfp(logfp, cconf)) { ret=-1; goto endfunc; } logp("Begin phase4 (shuffle files)\n"); write_status(client, STATUS_SHUFFLING, NULL, p1cntr, cntr); if(!lstat(current, &statp)) // Had a previous backup { unsigned long bno=0; FILE *fwd=NULL; int hardlinked=0; char tstmp[64]=""; int newdup=0; if(lstat(currentdup, &statp)) { // Have not duplicated the current backup yet. if(!lstat(currentduptmp, &statp)) { logp("Removing previous currentduptmp directory: %s\n", currentduptmp); if(recursive_delete(currentduptmp, NULL, TRUE /* del files */)) { logp("Could not delete %s\n", currentduptmp); ret=-1; goto endfunc; } } logp("Duplicating current backup.\n"); if(recursive_hardlink(current, currentduptmp, client, p1cntr, cntr, cconf) || do_rename(currentduptmp, currentdup)) { ret=-1; goto endfunc; } newdup++; } if(read_timestamp(timestamp, tstmp, sizeof(tstmp))) { logp("could not read timestamp file: %s\n", timestamp); ret=-1; goto endfunc; } // Get the backup number. bno=strtoul(tstmp, NULL, 10); // Put forward reference in, indicating the timestamp of // the working directory (which will soon become the current // directory). if(!(fwd=open_file(forward, "wb"))) { log_and_send("could not create forward file"); ret=-1; goto endfunc; } fprintf(fwd, "%s\n", tstmp); close_fp(&fwd); if(newdup) { // When we have just created currentdup, determine // hardlinked archive from the conf and the backup // number... hardlinked=do_hardlinked_archive(cconf, bno); } else { // ...if recovering, find out what currentdup started // out as. // Otherwise it is possible that things can be messed // up by somebody swapping between hardlinked and // not hardlinked at the same time as a resume happens. if(lstat(hlinkedpath, &statp)) { logp("previous attempt started not hardlinked\n"); hardlinked=0; } else { logp("previous attempt started hardlinked\n"); hardlinked=1; } } if(hardlinked) { // Create a file to indicate that the previous backup // does not have others depending on it. FILE *hfp=NULL; if(!(hfp=open_file(hlinkedpath, "wb"))) { ret=-1; goto endfunc; } // Stick the next backup timestamp in it. It might // be useful one day when wondering when the next // backup, now deleted, was made. fprintf(hfp, "%s\n", tstmp); close_fp(&hfp); logp(" doing hardlinked archive\n"); logp(" will not generate reverse deltas\n"); } else { logp(" not doing hardlinked archive\n"); logp(" will generate reverse deltas\n"); unlink(hlinkedpath); } if(atomic_data_jiggle(finishing, working, manifest, currentdup, currentdupdata, datadir, datadirtmp, cconf, client, hardlinked, bno, p1cntr, cntr)) { logp("could not finish up backup.\n"); ret=-1; goto endfunc; } write_status(client, STATUS_SHUFFLING, "deleting temporary files", p1cntr, cntr); // Remove the temporary data directory, we have now removed // everything useful from it. recursive_delete(datadirtmp, NULL, TRUE /* del files */); // Clean up the currentdata directory - this is now the 'old' // currentdata directory. Any files that were deleted from // the client will be left in there, so call recursive_delete // with the option that makes it not delete files. // This will have the effect of getting rid of unnecessary // directories. sync(); // try to help CIFS recursive_delete(currentdupdata, NULL, FALSE /* do not del files */); // Rename the old current to something that we know to // delete. if(do_rename(fullrealcurrent, deleteme)) { ret=-1; goto endfunc; } } else { // No previous backup, just put datadirtmp in the right place. if(do_rename(datadirtmp, datadir)) { ret=-1; goto endfunc; } } if(!lstat(deleteme, &statp)) { // Rename the currentdup directory... // IMPORTANT TODO: read the path to fullrealcurrent // from the deleteme timestamp. do_rename(currentdup, fullrealcurrent); recursive_delete(deleteme, NULL, TRUE /* delete all */); } // Rename the finishing symlink so that it becomes the current symlink do_rename(finishing, current); print_filecounters(p1cntr, cntr, ACTION_BACKUP, 0); logp("Backup completed.\n"); logp("End phase4 (shuffle files)\n"); set_logfp(NULL, cconf); // will close logfp. compress_filename(current, "log", "log.gz", cconf); endfunc: if(datadir) free(datadir); if(datadirtmp) free(datadirtmp); if(manifest) free(manifest); if(currentdup) free(currentdup); if(currentduptmp) free(currentduptmp); if(currentdupdata) free(currentdupdata); if(forward) free(forward); if(timestamp) free(timestamp); if(fullrealcurrent) free(fullrealcurrent); if(deleteme) free(deleteme); if(logpath) free(logpath); if(hlinkedpath) free(hlinkedpath); return ret; }