int send_msg_fzp(struct fzp *fzp, enum cmd cmd, const char *buf, size_t s) { if(fzp_printf(fzp, "%c%04X", cmd, (unsigned int)s)!=5 || fzp_write(fzp, buf, s)!=s || fzp_printf(fzp, "\n")!=1) { logp("Unable to write message to file: %s\n", strerror(errno)); return -1; } return 0; }
void logp(const char *fmt, ...) { #ifndef UTEST int pid; char buf[512]=""; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); pid=(int)getpid(); if(logfzp) fzp_printf(logfzp, "%s: %s[%d] %s", gettm(), prog, pid, buf); else { if(do_syslog) syslog(LOG_INFO, "%s", buf); if(do_stdout) { if(json) { char *cp=NULL; if((cp=strrchr(buf, '\n'))) *cp='\0'; // To help programs parsing the monitor output, // log things with simple JSON. fprintf(stdout, "{ \"logline\": \"%s\" }\n", buf); } else fprintf(stdout, "%s: %s[%d] %s", gettm(), prog, pid, buf); } } va_end(ap); #endif }
struct slist *build_manifest_with_data_files(const char *path, const char *datapath, int entries, int data_files) { struct blk *b=NULL; struct slist *slist=NULL; struct manio *manio=NULL; struct fzp *fzp=NULL; char spath[256]=""; char cpath[256]=""; fail_unless((manio=manio_open_phase3(path, "wb", PROTO_2, RMANIFEST_RELATIVE))!=NULL); slist=do_build_manifest(manio, PROTO_2, entries, data_files); fail_unless(!manio_close(&manio)); for(b=slist->blist->head; b; b=b->next) { snprintf(spath, sizeof(spath), "%s/%s", datapath, uint64_to_savepathstr(b->savepath)); if(strcmp(spath, cpath)) { snprintf(cpath, sizeof(cpath), "%s", spath); fzp_close(&fzp); } if(!fzp) { fail_unless(!build_path_w(cpath)); fail_unless((fzp=fzp_open(cpath, "wb"))!=NULL); } fzp_printf(fzp, "%c%04X%s", CMD_DATA, strlen("data"), "data"); } fzp_close(&fzp); return slist; }
static int gzprintf_hooks(struct fzp *fzp, struct hooks *hooks) { size_t i; fzp_printf(fzp, "%c%04lX%s\n", CMD_MANIFEST, strlen(hooks->path), hooks->path); for(i=0; i<hooks->len; i++) if(to_fzp_fingerprint(fzp, hooks->fingerprints[i])) return -1; return 0; }
static int gzprintf_hooks(struct fzp *fzp, struct hooks *hooks) { static char *f; static char ftmp[WEAK_STR_LEN]; size_t len=strlen(hooks->fingerprints); // printf("NW: %c%04lX%s\n", CMD_MANIFEST, // strlen(hooks->path), hooks->path); // FIX THIS: The path could be long, and fzp_printf will truncate at // 512 characters. fzp_printf(fzp, "%c%04lX%s\n", CMD_MANIFEST, strlen(hooks->path), hooks->path); for(f=hooks->fingerprints; f<hooks->fingerprints+len; f+=WEAK_LEN) { snprintf(ftmp, sizeof(ftmp), "%s", f); fzp_printf(fzp, "%c%04lX%s\n", CMD_FINGERPRINT, strlen(ftmp), ftmp); } return 0; }
static int write_hook_header(struct manio *manio, struct fzp *fzp, const char *comp) { const char *cp; char *tmp=NULL; cp=manio->rdirectory+strlen(manio->base_dir); while(cp && *cp=='/') cp++; if(!(tmp=prepend_s(cp, comp))) return -1; // FIX THIS: fzp_printf will truncate at 512 characters. fzp_printf(fzp, "%c%04X%s\n", CMD_MANIFEST, strlen(tmp), tmp); free_w(&tmp); return 0; }
// For the counters. void logc(const char *fmt, ...) { char buf[512]=""; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); if(logfzp) fzp_printf(logfzp, "%s", buf); // for the server side else { if(do_progress_counter && do_stdout) fprintf(stdout, "%s", buf); } va_end(ap); }
void build_sparse_index(const char *path, int manifests, int fingerprints) { struct fzp *fzp; fail_unless(!build_path_w(path)); fail_unless((fzp=fzp_gzopen(path, "wb"))!=NULL); for(int m=0; m<manifests; m++) { char mpath[256]; snprintf(mpath, sizeof(mpath), "some/manifest/%d", m); fzp_printf(fzp, "%c%04lX%s\n", CMD_MANIFEST, strlen(mpath), mpath); for(int f=0; f<fingerprints; f++) fail_unless(!to_fzp_fingerprint(fzp, prng_next64())); } fail_unless(!fzp_close(&fzp)); }
static int sort_and_write_hooks(struct manio *manio) { int i; int ret=-1; struct fzp *fzp=NULL; char comp[32]=""; char *path=NULL; int hook_count=manio->hook_count; char **hook_sort=manio->hook_sort; if(!hook_sort) return 0; snprintf(comp, sizeof(comp), "%08"PRIX64, manio->offset.fcount-1); if(!(path=prepend_s(manio->hook_dir, comp)) || build_path_w(path) || !(fzp=fzp_gzopen(path, manio->mode))) goto end; qsort(hook_sort, hook_count, sizeof(char *), strsort); if(write_hook_header(manio, fzp, comp)) goto end; for(i=0; i<hook_count; i++) { // Do not bother with duplicates. if(i && !strcmp(hook_sort[i], hook_sort[i-1])) continue; fzp_printf(fzp, "%c%04X%s\n", CMD_FINGERPRINT, (unsigned int)strlen(hook_sort[i]), hook_sort[i]); } if(fzp_close(&fzp)) { logp("Error closing %s in %s: %s\n", path, __func__, strerror(errno)); goto end; } if(manio_write_fcount(manio)) goto end; manio->hook_count=0; ret=0; end: fzp_close(&fzp); free_w(&path); return ret; }
static int write_incexc(const char *realworking, const char *incexc) { int ret=-1; struct fzp *fzp=NULL; char *path=NULL; if(!incexc || !*incexc) return 0; if(!(path=prepend_s(realworking, "incexc")) || !(fzp=fzp_open(path, "wb"))) goto end; fzp_printf(fzp, "%s", incexc); ret=0; end: if(fzp_close(&fzp)) { logp("error writing to %s in write_incexc\n", path); ret=-1; } free_w(&path); return ret; }
// Backup phase4 needs to know the fcount, so leave a file behind that // contains it (otherwise phase4 will have to read and sort the directory // contents). static int manio_write_fcount(struct manio *manio) { int ret=-1; struct fzp *fzp=NULL; char *path=NULL; if(!(path=get_fcount_path(manio)) || !(fzp=fzp_open(path, "wb"))) goto end; if(fzp_printf(fzp, "%08"PRIX64"\n", manio->offset->fcount)!=9) { logp("Short write when writing to %s\n", path); goto end; } ret=0; end: if(fzp_close(&fzp)) { logp("Could not close file pointer to %s\n", path); ret=-1; } free_w(&path); return ret; }
int backup_phase4_server_protocol1(struct sdirs *sdirs, struct conf **cconfs) { int ret=-1; struct stat statp; char realcurrent[256]=""; uint64_t bno=0; int hardlinked_current=0; char tstmp[64]=""; int previous_backup=0; struct fdirs *fdirs=NULL; readlink_w(sdirs->current, realcurrent, sizeof(realcurrent)); if(!(fdirs=fdirs_alloc()) || fdirs_init(fdirs, sdirs, realcurrent)) goto end; if(log_fzp_set(fdirs->logpath, cconfs)) goto end; logp("Begin phase4 (shuffle files)\n"); if(write_status(CNTR_STATUS_SHUFFLING, NULL, get_cntr(cconfs))) 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)) { logp("Could not delete %s\n", fdirs->currentduptmp); goto end; } } logp("Duplicating current backup.\n"); if(recursive_hardlink(sdirs->current, fdirs->currentduptmp, cconfs) // 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=strtoull(tstmp, NULL, 10); // Determine whether the new backup should be a hardlinked // archive or not, from the confs and the backup number... if(need_hardlinked_archive(cconfs, bno)) { // Create a file to indicate that the previous backup // does not have others depending on it. struct fzp *hfp=NULL; if(!(hfp=fzp_open(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. fzp_printf(hfp, "%s\n", tstmp); if(fzp_close(&hfp)) { logp("error closing hardlinked indication\n"); goto end; } } else unlink(fdirs->hlinked); if(atomic_data_jiggle(sdirs, fdirs, hardlinked_current, cconfs)) { logp("could not finish up backup.\n"); goto end; } if(write_status(CNTR_STATUS_SHUFFLING, "deleting temporary files", get_cntr(cconfs))) goto end; // Remove the temporary data directory, we have now removed // everything useful from it. recursive_delete(fdirs->datadirtmp); // 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. recursive_delete_dirs_only(fdirs->currentdupdata); // Rename the old current to something that we know to delete. if(previous_backup && !hardlinked_current) { if(deleteme_move(sdirs, fdirs->fullrealcurrent, realcurrent, cconfs) // 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(cconfs, sdirs)) goto end; logp("End phase4 (shuffle files)\n"); ret=0; end: fdirs_free(&fdirs); return ret; }