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; }