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; }
int recursive_hardlink(const char *src, const char *dst, struct conf **confs) { int n=-1; int ret=0; struct dirent **dir; char *tmp=NULL; char *fullpatha=NULL; char *fullpathb=NULL; //logp("in rec hl: %s %s\n", src, dst); if(!(tmp=prepend_s(dst, "dummy"))) return -1; if(mkpath(&tmp, dst)) { logp("could not mkpath for %s\n", tmp); free_w(&tmp); return -1; } free_w(&tmp); if((n=scandir(src, &dir, 0, 0))<0) { logp("recursive_hardlink scandir %s: %s\n", src, strerror(errno)); return -1; } while(n--) { struct stat statp; if(dir[n]->d_ino==0 || !strcmp(dir[n]->d_name, ".") || !strcmp(dir[n]->d_name, "..")) { free(dir[n]); continue; } free_w(&fullpatha); free_w(&fullpathb); if(!(fullpatha=prepend_s(src, dir[n]->d_name)) || !(fullpathb=prepend_s(dst, dir[n]->d_name))) break; #ifdef _DIRENT_HAVE_D_TYPE // Faster evaluation on most systems. if(dir[n]->d_type==DT_DIR) { if(recursive_hardlink(fullpatha, fullpathb, confs)) break; } else #endif // Otherwise, we have to do an lstat() anyway, because we // will need to check the number of hardlinks in do_link(). if(lstat(fullpatha, &statp)) { logp("could not lstat %s\n", fullpatha); } else if(S_ISDIR(statp.st_mode)) { if(recursive_hardlink(fullpatha, fullpathb, confs)) break; } else { //logp("hardlinking %s to %s\n", fullpathb, fullpatha); if(write_status(CNTR_STATUS_SHUFFLING, fullpathb, get_cntr(confs)) || do_link(fullpatha, fullpathb, &statp, confs, 0 /* do not overwrite target */)) break; } free(dir[n]); } if(n>0) { ret=-1; for(; n>0; n--) free(dir[n]); } free(dir); free_w(&fullpatha); free_w(&fullpathb); 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; }
int recursive_hardlink(const char *src, const char *dst, const char *client, struct cntr *p1cntr, struct cntr *cntr, struct config *conf) { int n=-1; int ret=0; struct dirent **dir; char *tmp=NULL; //logp("in rec hl: %s %s\n", src, dst); if(!(tmp=prepend_s(dst, "dummy", strlen("dummy")))) return -1; if(mkpath(&tmp, dst)) { logp("could not mkpath for %s\n", tmp); free(tmp); return -1; } free(tmp); if((n=scandir(src, &dir, 0, 0))<0) { logp("recursive_hardlink scandir %s: %s\n", src, strerror(errno)); return -1; } while(n--) { struct stat statp; char *fullpatha=NULL; char *fullpathb=NULL; if(dir[n]->d_ino==0 || !strcmp(dir[n]->d_name, ".") || !strcmp(dir[n]->d_name, "..")) { free(dir[n]); continue; } if(!(fullpatha=prepend_s(src, dir[n]->d_name, strlen(dir[n]->d_name))) || !(fullpathb=prepend_s(dst, dir[n]->d_name, strlen(dir[n]->d_name)))) { if(fullpatha) free(fullpatha); if(fullpathb) free(fullpathb); break; } if(lstat(fullpatha, &statp)) { logp("could not lstat %s\n", fullpatha); } else if(S_ISDIR(statp.st_mode)) { if(recursive_hardlink(fullpatha, fullpathb, client, p1cntr, cntr, conf)) { free(fullpatha); free(fullpathb); break; } } else { //logp("hardlinking %s to %s\n", fullpathb, fullpatha); write_status(client, STATUS_SHUFFLING, fullpathb, p1cntr, cntr); if(do_link(fullpatha, fullpathb, &statp, conf, FALSE /* do not overwrite target */)) { free(fullpatha); free(fullpathb); break; } } free(fullpatha); free(fullpathb); free(dir[n]); } if(n>0) { ret=-1; for(; n>0; n--) free(dir[n]); } free(dir); return ret; }