static int actual_restore(struct asfd *asfd, struct bu *bu, const char *manifest, regex_t *regex, int srestore, enum action act, struct sdirs *sdirs, enum cntr_status cntr_status, struct conf **cconfs) { int ret=-1; int do_restore_stream=1; // For out-of-sequence directory restoring so that the // timestamps come out right: struct slist *slist=NULL; if(linkhash_init() || !(slist=slist_alloc())) goto end; if(get_protocol(cconfs)==PROTO_2) { switch(maybe_restore_spool(asfd, manifest, sdirs, bu, srestore, regex, cconfs, slist, act, cntr_status)) { case 1: do_restore_stream=0; break; case 0: do_restore_stream=1; break; default: goto end; // Error; } } if(do_restore_stream && restore_stream(asfd, sdirs, slist, bu, manifest, regex, srestore, cconfs, act, cntr_status)) goto end; if(restore_remaining_dirs(asfd, bu, slist, act, sdirs, cntr_status, cconfs)) goto end; // Restore has nearly completed OK. ret=restore_end(asfd, cconfs); cntr_print(get_cntr(cconfs), act); cntr_stats_to_file(get_cntr(cconfs), bu->path, act, cconfs); end: slist_free(&slist); linkhash_free(); return ret; }
static int do_backup_server(struct async *as, struct sdirs *sdirs, struct conf **cconfs, const char *incexc, int resume) { int ret=0; int do_phase2=1; struct asfd *asfd=as->asfd; enum protocol protocol=get_protocol(cconfs); struct cntr *cntr=get_cntr(cconfs); logp("in do_backup_server\n"); log_rshash(cconfs); if(resume) { if(sdirs_get_real_working_from_symlink(sdirs) || sdirs_get_real_manifest(sdirs, protocol) || open_log(asfd, sdirs, cconfs)) goto error; } else { // Not resuming - need to set everything up fresh. if(sdirs_create_real_working(sdirs, get_string(cconfs[OPT_TIMESTAMP_FORMAT])) || sdirs_get_real_manifest(sdirs, protocol) || open_log(asfd, sdirs, cconfs)) goto error; if(write_incexc(sdirs->rworking, incexc)) { logp("unable to write incexc\n"); goto error; } if(backup_phase1_server(as, sdirs, cconfs)) { logp("error in phase 1\n"); goto error; } } if(resume) { struct stat statp; if(lstat(sdirs->phase1data, &statp) && !lstat(sdirs->changed, &statp) && !lstat(sdirs->unchanged, &statp)) { // In this condition, it looks like there was an // interruption during phase3. Skip phase2. do_phase2=0; } } if(do_phase2) { if(backup_phase2_server(as, sdirs, incexc, resume, cconfs)) { logp("error in backup phase 2\n"); goto error; } asfd->write_str(asfd, CMD_GEN, "okbackupend"); } // Close the connection with the client, the rest of the job we can do // by ourselves. logp("Backup ending - disconnect from client.\n"); if(asfd_flush_asio(asfd)) goto end; as->asfd_remove(as, asfd); asfd_close(asfd); if(backup_phase3_server(sdirs, cconfs)) { logp("error in backup phase 3\n"); goto error; } if(do_rename(sdirs->working, sdirs->finishing)) goto error; if(backup_phase4_server(sdirs, cconfs)) { logp("error in backup phase 4\n"); goto error; } cntr_print(cntr, ACTION_BACKUP, asfd); cntr_stats_to_file(cntr, sdirs->rworking, ACTION_BACKUP); if(protocol==PROTO_2) { // Regenerate dindex before the symlink is renamed, so that the // champ chooser cleanup does not try to remove data files // whilst the dindex regeneration is happening. if(regenerate_client_dindex(sdirs)) goto error; } // Move the symlink to indicate that we are now in the end phase. The // rename() race condition is automatically recoverable here. if(do_rename(sdirs->finishing, sdirs->current)) goto error; logp("Backup completed.\n"); log_fzp_set(NULL, cconfs); compress_filename(sdirs->rworking, "log", "log.gz", get_int(cconfs[OPT_COMPRESSION])); goto end; error: ret=-1; end: log_fzp_set(NULL, cconfs); return ret; }
int backup_phase4_server_burp1(struct sdirs *sdirs, struct conf *cconf) { int ret=-1; struct stat statp; ssize_t len=0; char realcurrent[256]=""; unsigned long bno=0; int hardlinked_current=0; char tstmp[64]=""; int previous_backup=0; struct fdirs *fdirs=NULL; if((len=readlink(sdirs->current, realcurrent, sizeof(realcurrent)-1))<0) len=0; realcurrent[len]='\0'; if(!(fdirs=fdirs_alloc()) || fdirs_init(fdirs, sdirs, realcurrent)) goto end; if(set_logfp(fdirs->logpath, cconf)) goto end; logp("Begin phase4 (shuffle files)\n"); if(write_status(STATUS_SHUFFLING, NULL, cconf)) goto end; if(!lstat(sdirs->current, &statp)) // Had a previous backup. { previous_backup++; if(lstat(fdirs->hlinkedcurrent, &statp)) { hardlinked_current=0; logp("Previous backup is not a hardlinked_archive\n"); logp(" will generate reverse deltas\n"); } else { hardlinked_current=1; logp("Previous backup is a hardlinked_archive\n"); logp(" will not generate reverse deltas\n"); } // If current was not a hardlinked_archive, need to duplicate // it. if(!hardlinked_current && lstat(fdirs->currentdup, &statp)) { // Have not duplicated the current backup yet. if(!lstat(fdirs->currentduptmp, &statp)) { logp("Removing previous directory: %s\n", fdirs->currentduptmp); if(recursive_delete(fdirs->currentduptmp, NULL, 1 /* del files */)) { logp("Could not delete %s\n", fdirs->currentduptmp); goto end; } } logp("Duplicating current backup.\n"); if(recursive_hardlink(sdirs->current, fdirs->currentduptmp, cconf) // The rename race condition is of no consequence here // because currentdup does not exist. || do_rename(fdirs->currentduptmp, fdirs->currentdup)) goto end; } } if(timestamp_read(fdirs->timestamp, tstmp, sizeof(tstmp))) { logp("could not read timestamp file: %s\n", fdirs->timestamp); goto end; } // Get the backup number. bno=strtoul(tstmp, NULL, 10); // Determine whether the new backup should be a hardlinked // archive or not, from the conf and the backup number... if(need_hardlinked_archive(cconf, bno)) { // Create a file to indicate that the previous backup // does not have others depending on it. FILE *hfp=NULL; if(!(hfp=open_file(fdirs->hlinked, "wb"))) goto end; // 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); if(close_fp(&hfp)) { logp("error closing hardlinked indication\n"); goto end; } } else unlink(fdirs->hlinked); if(atomic_data_jiggle(sdirs, fdirs, hardlinked_current, cconf, bno)) { logp("could not finish up backup.\n"); goto end; } if(write_status(STATUS_SHUFFLING, "deleting temporary files", cconf)) goto end; // Remove the temporary data directory, we have now removed // everything useful from it. recursive_delete(fdirs->datadirtmp, NULL, 1 /* 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(fdirs->currentdupdata, NULL, 0 /* do not del files */); // Rename the old current to something that we know to delete. if(previous_backup && !hardlinked_current) { if(deleteme_move(sdirs->client, fdirs->fullrealcurrent, realcurrent, cconf) // I have tested that potential race conditions on the // rename() are automatically recoverable here. || do_rename(fdirs->currentdup, fdirs->fullrealcurrent)) goto end; } if(deleteme_maybe_delete(cconf, sdirs->client)) goto end; cntr_stats_to_file(cconf->cntr, sdirs->finishing, ACTION_BACKUP); logp("End phase4 (shuffle files)\n"); ret=0; end: fdirs_free(fdirs); return ret; }
int backup_phase4_server(struct sdirs *sdirs, struct conf *cconf) { int ret=-1; struct stat statp; char *manifest=NULL; char *deletionsfile=NULL; char *datadir=NULL; char *datadirtmp=NULL; char *currentdup=NULL; char *currentduptmp=NULL; char *currentdupdata=NULL; char *timestamp=NULL; char *fullrealcurrent=NULL; char *logpath=NULL; char *hlinkedpath=NULL; ssize_t len=0; char realcurrent[256]=""; unsigned long bno=0; int hardlinked=0; char tstmp[64]=""; int newdup=0; int previous_backup=0; if((len=readlink(sdirs->current, realcurrent, sizeof(realcurrent)-1))<0) len=0; realcurrent[len]='\0'; if(!(datadir=prepend_s(sdirs->finishing, "data")) || !(datadirtmp=prepend_s(sdirs->finishing, "data.tmp")) || !(manifest=prepend_s(sdirs->finishing, "manifest.gz")) || !(deletionsfile=prepend_s(sdirs->finishing, "deletions")) || !(currentdup=prepend_s(sdirs->finishing, "currentdup")) || !(currentduptmp=prepend_s(sdirs->finishing, "currentdup.tmp")) || !(currentdupdata=prepend_s(currentdup, "data")) || !(timestamp=prepend_s(sdirs->finishing, "timestamp")) || !(fullrealcurrent=prepend_s(sdirs->client, realcurrent)) || !(logpath=prepend_s(sdirs->finishing, "log")) || !(hlinkedpath=prepend_s(currentdup, "hardlinked"))) goto end; if(set_logfp(logpath, cconf)) goto end; logp("Begin phase4 (shuffle files)\n"); if(write_status(STATUS_SHUFFLING, NULL, cconf)) goto end; if(!lstat(sdirs->current, &statp)) // Had a previous backup { previous_backup++; 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, 1 /* del files */)) { logp("Could not delete %s\n", currentduptmp); goto end; } } logp("Duplicating current backup.\n"); if(recursive_hardlink(sdirs->current, currentduptmp, cconf) || do_rename(currentduptmp, currentdup)) goto end; newdup++; } if(read_timestamp(timestamp, tstmp, sizeof(tstmp))) { logp("could not read timestamp file: %s\n", timestamp); goto end; } // Get the backup number. bno=strtoul(tstmp, NULL, 10); 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"))) goto end; // 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); if(close_fp(&hfp)) { logp("error closing hardlinked indication\n"); goto end; } 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(sdirs, cconf, manifest, currentdup, currentdupdata, datadir, datadirtmp, deletionsfile, hardlinked, bno)) { logp("could not finish up backup.\n"); goto end; } if(write_status(STATUS_SHUFFLING, "deleting temporary files", cconf)) goto end; // Remove the temporary data directory, we have now removed // everything useful from it. recursive_delete(datadirtmp, NULL, 1 /* 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, 0 /* do not del files */); // Rename the old current to something that we know to delete. if(previous_backup) { if(deleteme_move(sdirs->client, fullrealcurrent, realcurrent, cconf) || do_rename(currentdup, fullrealcurrent)) goto end; } if(deleteme_maybe_delete(cconf, sdirs->client)) goto end; cntr_stats_to_file(cconf->cntr, sdirs->finishing, ACTION_BACKUP); // Rename the finishing symlink so that it becomes the current symlink do_rename(sdirs->finishing, sdirs->current); cntr_print(cconf->cntr, ACTION_BACKUP); logp("Backup completed.\n"); logp("End phase4 (shuffle files)\n"); set_logfp(NULL, cconf); // will close logfp. compress_filename(sdirs->current, "log", "log.gz", cconf); ret=0; end: if(datadir) free(datadir); if(datadirtmp) free(datadirtmp); if(manifest) free(manifest); if(deletionsfile) free(deletionsfile); if(currentdup) free(currentdup); if(currentduptmp) free(currentduptmp); if(currentdupdata) free(currentdupdata); if(timestamp) free(timestamp); if(fullrealcurrent) free(fullrealcurrent); if(logpath) free(logpath); if(hlinkedpath) free(hlinkedpath); return ret; }