static char *get_ca_dir(struct conf *conf) { FILE *fp=NULL; char buf[4096]=""; if(!(fp=open_file(conf->ca_conf, "r"))) return NULL; while(fgets(buf, sizeof(buf), fp)) { char *field=NULL; char *value=NULL; if(conf_get_pair(buf, &field, &value) || !field || !value) continue; if(!strcasecmp(field, "CA_DIR")) { if(!(gca_dir=strdup(value))) { log_out_of_memory(__func__); fclose(fp); return NULL; } break; } } fclose(fp); return gca_dir; }
static char *json_escape(const char *str) { int i; int j; int n=0; char *estr=NULL; const char echars[]="\\\""; if(!str) return NULL; n=strlen(str); estr=(char *)malloc(2*n*sizeof(char)); if(!estr) { log_out_of_memory(__func__); return NULL; } for(i=0, j=0; i<n; i++, j++) { int k=sizeof(echars); for(; k && str[i]!=echars[k-1]; k--); if(k) estr[j++]='\\'; estr[j]=str[i]; } estr[j]='\0'; return estr; }
static char *get_next_str(struct asfd *asfd, char **data, size_t *l, struct conf *conf, ssize_t *s, const char *path) { char *ret=NULL; if((sscanf(*data, "%08X", (unsigned int *)s))!=1) { logw(asfd, conf, "sscanf of xattr '%s' %d failed for %s\n", *data, *l, path); return NULL; } *data+=8; *l-=8; if(!(ret=(char *)malloc((*s)+1))) { log_out_of_memory(__func__); return NULL; } memcpy(ret, *data, *s); ret[*s]='\0'; *data+=*s; *l-=*s; return ret; }
int check_browsedir(const char *browsedir, struct sbuf *mb, size_t bdlen, char **last_bd_match) { char *cp=mb->path.buf; char *copy=NULL; if(bdlen>0) { if(strncmp(browsedir, cp, bdlen)) return 0; cp+=bdlen; if(browsedir[bdlen-1]!='/') { if(*cp!='\0') { if(*cp!='/') return 0; cp++; } } } if(*cp=='\0') cp=(char *)"."; if(!(copy=strdup_w(cp, __func__))) goto error; if((cp=strchr(copy, '/'))) { if(bdlen==0) cp++; *cp='\0'; maybe_fake_directory(mb); } else if(!strcmp(mb->path.buf, "/") && !strcmp(browsedir, "/")) maybe_fake_directory(mb); else if(mb->path.cmd==CMD_DIRECTORY) maybe_fake_directory(mb); // Strip off possible trailing slash. if((cp=strrchr(copy, '/')) && cp>copy) *cp='\0'; if(*last_bd_match && !strcmp(*last_bd_match, copy)) { // Got a duplicate match. free_w(©); return 0; } free_w(&mb->path.buf); mb->path.buf=copy; free_w(last_bd_match); if(!(*last_bd_match=strdup_w(copy, __func__))) goto error; return 1; error: free_w(©); log_out_of_memory(__func__); return -1; }
static int get_files_in_directory(DIR *directory, struct dirent ***nl, int *count) { int status; int allocated=0; struct dirent **ntmp=NULL; struct dirent *entry=NULL; struct dirent *result=NULL; /* Graham says: this here is doing a funky kind of scandir/alphasort that can also run on Windows. TODO: split into a scandir function */ while(1) { char *p; if(!(entry=(struct dirent *)malloc( sizeof(struct dirent)+name_max+100))) { log_out_of_memory(__func__); return -1; } status=readdir_r(directory, entry, &result); if(status || !result) { free(entry); break; } p=entry->d_name; ASSERT(name_max+1 > (int)sizeof(struct dirent)+strlen(p)); /* Skip `.', `..', and excluded file names. */ if(!p || !strcmp(p, ".") || !strcmp(p, "..")) { free(entry); continue; } if(*count==allocated) { if(!allocated) allocated=10; else allocated*=2; if(!(ntmp=(struct dirent **) realloc_w(*nl, allocated*sizeof(**nl), __func__))) { free(entry); return -1; } *nl=ntmp; } (*nl)[(*count)++]=entry; } if(*nl) qsort(*nl, *count, sizeof(**nl), (int (*)(const void *, const void *))myalphasort); return 0; }
int check_browsedir(const char *browsedir, char **path, size_t bdlen, char **last_bd_match) { char *cp=NULL; char *copy=NULL; if(strncmp(browsedir, *path, bdlen)) return 0; if((*path)[bdlen+1]=='\0' || (bdlen>1 && (*path)[bdlen]!='/')) return 0; /* Lots of messing around related to whether browsedir was '', '/', or something else. */ if(*browsedir) { if(!strcmp(browsedir, "/")) { if(!(copy=strdup_w((*path)+bdlen, __func__))) goto error; if((cp=strchr(copy+1, '/'))) *cp='\0'; } else { if(!(copy=strdup_w((*path)+bdlen+1, __func__))) goto error; if((cp=strchr(copy, '/'))) *cp='\0'; } } else { if(!(copy=strdup_w((*path)+bdlen, __func__))) goto error; if(*copy=='/') *(copy+1)='\0'; // Messing around for Windows. else if(strlen(copy)>2 && copy[1]==':' && copy[2]=='/') copy[2]='\0'; } if(last_bd_match && *last_bd_match) { if(!strcmp(*last_bd_match, copy)) { // Got a duplicate match. free(copy); return 0; } free(*last_bd_match); } free(*path); *path=copy; if(!(*last_bd_match=strdup_w(copy, __func__))) goto error; return 1; error: if(copy) free(copy); log_out_of_memory(__func__); return -1; }
// When a backup is ongoing, use this to add newly complete candidates. int candidate_add_fresh(const char *path, struct conf *conf) { int ars; int ret=-1; gzFile zp=NULL; const char *cp=NULL; struct sbuf *sb=NULL; struct candidate *candidate=NULL; struct blk *blk=NULL; if(!(candidate=candidates_add_new())) goto end; cp=path+strlen(conf->directory); while(cp && *cp=='/') cp++; if(!(candidate->path=strdup(cp))) { log_out_of_memory(__func__); goto end; } if(!(sb=sbuf_alloc(conf)) || !(blk=blk_alloc()) || !(zp=gzopen_file(path, "rb"))) goto end; while(zp) { if((ars=sbuf_fill_from_gzfile(sb, NULL /* struct async */, zp, blk, NULL, conf))<0) goto end; else if(ars>0) { // Reached the end. break; } if(!*(blk->weak)) continue; if(is_hook(blk->weak)) { if(sparse_add_candidate(blk->weak, candidate)) goto end; } *blk->weak='\0'; } if(scores_grow(scores, candidates_len)) goto end; candidates_set_score_pointers(candidates, candidates_len, scores); scores_reset(scores); //printf("HERE: %d candidates\n", (int)candidates_len); ret=0; end: gzclose_fp(&zp); sbuf_free(sb); blk_free(blk); return ret; }
static int replace_conf_str(const char *newval, char **dest) { if(newval) { if(*dest) free(*dest); if(!(*dest=strdup(newval))) { log_out_of_memory(__func__); return -1; } } return 0; }
static int async_alloc_buf(char **buf, size_t *buflen, size_t bufmaxsize) { if(!*buf) { if(!(*buf=(char *)malloc(bufmaxsize))) { log_out_of_memory(__FUNCTION__); return -1; } truncate_buf(buf, buflen); } return 0; }
static int get_link(const char *basedir, const char *lnk, char real[], size_t r) { int len=0; char *tmp=NULL; if(!(tmp=prepend_s(basedir, lnk, strlen(lnk)))) { log_out_of_memory(__FUNCTION__); return -1; } if((len=readlink(tmp, real, r-1))<0) len=0; real[len]='\0'; free(tmp); return 0; }
static int add_to_backup_list(char ***backups, int *b, const char *tok) { const char *str=NULL; if(!(str=get_backup_str(tok))) return 0; if(!(*backups=(char **)realloc(*backups, ((*b)+2)*sizeof(char *))) || !((*backups)[*b]=strdup(str))) { log_out_of_memory(__FUNCTION__); return -1; } (*backups)[(*b)+1]=NULL; (*b)++; return 0; }
static int get_link(const char *basedir, const char *lnk, char real[], size_t r) { int len=0; char *tmp=NULL; if(!(tmp=prepend(basedir, lnk, "/"))) { log_out_of_memory(__func__); return -1; } if((len=readlink(tmp, real, r-1))<0) len=0; real[len]='\0'; free_w(&tmp); // Strip any trailing slash. if(real[strlen(real)-1]=='/') real[strlen(real)-1]='\0'; return 0; }
// Return -1 on error, 0 on OK, 1 for srcmanio finished. int manio_copy_entry(struct asfd *asfd, struct sbuf **csb, struct sbuf *sb, struct blk **blk, struct manio *srcmanio, struct manio *dstmanio, struct conf *conf) { static int ars; static char *copy=NULL; // Use the most recent stat for the new manifest. if(dstmanio && manio_write_sbuf(dstmanio, sb)) goto error; if(!(copy=strdup((*csb)->path.buf))) { log_out_of_memory(__func__); goto error; } while(1) { if((ars=manio_sbuf_fill(srcmanio, asfd, *csb, *blk, NULL, conf))<0) goto error; else if(ars>0) { // Finished. sbuf_free(*csb); *csb=NULL; blk_free(*blk); *blk=NULL; free(copy); return 1; } // Got something. if(strcmp((*csb)->path.buf, copy)) { // Found the next entry. free(copy); return 0; } // Should have the next signature. // Write it to the destination manifest. if(dstmanio && manio_write_sig_and_path(dstmanio, *blk)) goto error; } error: if(copy) free(copy); return -1; }
static int setenv_x509_date(ASN1_TIME *tm, const char *env) { BIO *bio_out=NULL; BUF_MEM *bptr=NULL; char tmpbuf[256]=""; if(!(bio_out=BIO_new(BIO_s_mem()))) { log_out_of_memory(__func__); return -1; } ASN1_TIME_print(bio_out, tm); BIO_get_mem_ptr(bio_out, &bptr); BIO_gets(bio_out, tmpbuf, sizeof(tmpbuf)-1); BIO_free_all(bio_out); sanitise(tmpbuf); setenv(env, (char*)tmpbuf, 1); return 0; }
static int setenv_x509_serialnumber(ASN1_INTEGER *i, const char *env) { BIO *bio_out=NULL; BUF_MEM *bptr=NULL; char tmpbuf[256]=""; if(!(bio_out=BIO_new(BIO_s_mem()))) { log_out_of_memory(__func__); return -1; } i2a_ASN1_INTEGER(bio_out, i); BIO_get_mem_ptr(bio_out, &bptr); BIO_gets(bio_out, tmpbuf, sizeof(tmpbuf)-1); BIO_free_all(bio_out); sanitise(tmpbuf); setenv(env, (char*)tmpbuf, 1); return 0; }
static int deal_with_dot_inclusion(const char *conf_path, char **extrafile, struct conf **confs) { int ret=-1; char *copy=NULL; #ifndef HAVE_WIN32 int i=0; glob_t globbuf; if(**extrafile!='/') #else if(strlen(*extrafile)>2 && (*extrafile)[1]!=':') #endif { // It is relative to the directory that the // current conf file is in. char *cp=NULL; char *tmp=NULL; if(!(copy=strdup_w(conf_path, __func__))) goto end; if((cp=strrchr(copy, '/'))) *cp='\0'; if(!(tmp=prepend_s(copy, *extrafile))) { log_out_of_memory(__func__); goto end; } free_w(extrafile); *extrafile=tmp; } #ifndef HAVE_WIN32 // Treat it is a glob expression. memset(&globbuf, 0, sizeof(globbuf)); glob(*extrafile, 0, NULL, &globbuf); for(i=0; (unsigned int)i<globbuf.gl_pathc; i++) if((ret=conf_load_lines_from_file(globbuf.gl_pathv[i], confs))) goto end; #else ret=conf_load_lines_from_file(*extrafile, confs); #endif end: free_w(©); return ret; }
static int gen_rev_delta(const char *sigpath, const char *deltadir, const char *oldpath, const char *finpath, const char *path, struct sbuf *sb, struct conf *cconf) { int ret=-1; char *delpath=NULL; if(!(delpath=prepend_s(deltadir, path))) { log_out_of_memory(__func__); goto end; } //logp("Generating reverse delta...\n"); /* logp("delpath: %s\n", delpath); logp("finpath: %s\n", finpath); logp("sigpath: %s\n", sigpath); logp("oldpath: %s\n", oldpath); */ if(mkpath(&delpath, deltadir)) { logp("could not mkpaths for: %s\n", delpath); goto end; } else if(make_rev_sig(finpath, sigpath, sb->burp1->endfile.buf, sb->compression, cconf)) { logp("could not make signature from: %s\n", finpath); goto end; } else if(make_rev_delta(oldpath, sigpath, delpath, sb->compression, cconf)) { logp("could not make delta from: %s\n", oldpath); goto end; } else unlink(sigpath); ret=0; end: if(delpath) free(delpath); return ret; }
int check_browsedir(const char *browsedir, char **path, size_t bdlen) { char *cp=NULL; char *copy=NULL; if(strncmp(browsedir, *path, bdlen)) return 0; if((*path)[bdlen+1]=='\0') return 0; /* Lots of messing around related to whether browsedir was '', '/', or something else. */ if(*browsedir) { if(!strcmp(browsedir, "/")) { if(!(copy=strdup((*path)+bdlen))) goto err; if((cp=strchr(copy+1, '/'))) *cp='\0'; } else { if(!(copy=strdup((*path)+bdlen+1))) goto err; if((cp=strchr(copy, '/'))) *cp='\0'; } } else { if(!(copy=strdup((*path)+bdlen))) goto err; if(*copy=='/') *(copy+1)='\0'; // Messing around for Windows. else if(strlen(copy)>2 && copy[1]==':' && copy[2]=='/') copy[2]='\0'; } free(*path); *path=copy; return 1; err: if(copy) free(copy); log_out_of_memory(__FUNCTION__); return -1; }
static int parse_readbuf(char *cmd, char **dest, size_t *rlen) { unsigned int s=0; char cmdtmp='\0'; if(readbuflen>=5) { if((sscanf(readbuf, "%c%04X", &cmdtmp, &s))!=2) { logp("sscanf of '%s' failed in parse_readbuf\n", readbuf); truncate_buf(&readbuf, &readbuflen); return -1; } } if(readbuflen>=s+5) { *cmd=cmdtmp; if(!(*dest=(char *)malloc(s+1))) { log_out_of_memory(__FUNCTION__); truncate_buf(&readbuf, &readbuflen); return -1; } if(!(memcpy(*dest, readbuf+5, s))) { logp("memcpy failed in parse_readbuf\n"); truncate_buf(&readbuf, &readbuflen); return -1; } (*dest)[s]='\0'; if(!(memmove(readbuf, readbuf+s+5, readbuflen-s-5))) { logp("memmove failed in parse_readbuf\n"); truncate_buf(&readbuf, &readbuflen); return -1; } readbuflen-=s+5; *rlen=s; } return 0; }
static int make_link(struct asfd *asfd, const char *fname, const char *lnk, enum cmd cmd, struct cntr *cntr, const char *restore_prefix) { int ret=-1; #ifdef HAVE_WIN32 logw(asfd, cntr, "windows seems not to support hardlinks or symlinks\n"); #else unlink(fname); if(cmd==CMD_HARD_LINK) { char *flnk=NULL; if(!(flnk=prepend_s(restore_prefix, lnk))) { log_out_of_memory(__func__); return -1; } //printf("%s -> %s\n", fname, flnk); ret=link(flnk, fname); free_w(&flnk); } else if(cmd==CMD_SOFT_LINK) { //printf("%s -> %s\n", fname, lnk); ret=symlink(lnk, fname); } else { logp("unexpected link command: %c\n", cmd); ret=-1; } #endif if(ret) logp("could not %slink %s -> %s: %s\n", cmd==CMD_HARD_LINK?"hard":"sym", fname, lnk, strerror(errno)); return ret; }
static int looks_like_protocol1(const char *basedir) { int ret=-1; char *tmp=NULL; struct stat statp; if(!(tmp=prepend_s(basedir, "current"))) { log_out_of_memory(__func__); goto end; } // If there is a 'current' symlink here, we think it looks like a // protocol 1 backup. if(!lstat(tmp, &statp) && S_ISLNK(statp.st_mode)) { ret=1; goto end; } ret=0; end: free_w(&tmp); return ret; }
/* Make it atomic by linking to a temporary file, then moving it into place. */ static int do_hardlink(struct file *o, struct file *n, const char *ext) { int ret=-1; char *tmppath=NULL; if(!(tmppath=prepend(o->path, ext, ""))) { log_out_of_memory(__func__); goto end; } if(link(n->path, tmppath)) { logp("Could not hardlink %s to %s: %s\n", tmppath, n->path, strerror(errno)); goto end; } if((ret=do_rename(tmppath, o->path))) goto end; ret=0; end: free_w(&tmppath); return ret; }
static int gen_rev_delta(const char *sigpath, const char *deltadir, const char *oldpath, const char *finpath, const char *path, const char *endfile, int compression, struct cntr *cntr, struct config *cconf) { int ret=0; char *delpath=NULL; if(!(delpath=prepend_s(deltadir, path, strlen(path)))) { log_out_of_memory(__FUNCTION__); ret=-1; } //logp("Generating reverse delta...\n"); /* logp("delpath: %s\n", delpath); logp("finpath: %s\n", finpath); logp("sigpath: %s\n", sigpath); logp("oldpath: %s\n", oldpath); */ if(mkpath(&delpath, deltadir)) { logp("could not mkpaths for: %s\n", delpath); ret=-1; } else if(make_rev_sig(finpath, sigpath, endfile, compression, cntr)) { logp("could not make signature from: %s\n", finpath); ret=-1; } else if(make_rev_delta(oldpath, sigpath, delpath, compression, cntr, cconf)) { logp("could not make delta from: %s\n", oldpath); ret=-1; } else unlink(sigpath); if(delpath) free(delpath); return ret; }
int str_to_counters(const char *str, char **client, char *status, char *phase, char **path, struct cntr *p1cntr, struct cntr *cntr, char ***backups) { int t=0; char *tok=NULL; char *copy=NULL; reset_filecounter(p1cntr, 0); reset_filecounter(cntr, 0); if(!(copy=strdup(str))) { log_out_of_memory(__FUNCTION__); return -1; } if((tok=strtok(copy, "\t\n"))) { char *counter_version=NULL; if(client && !(*client=strdup(tok))) { log_out_of_memory(__FUNCTION__); return -1; } if(!(counter_version=strtok(NULL, "\t\n"))) { free(copy); return 0; } // First token after the client name is the version of // the counter parser thing, which now has to be noted // because counters might be passed to the client instead // of just the server status monitor. if(*counter_version==COUNTER_VERSION_2 || *counter_version==COUNTER_VERSION_1) // old version { while(1) { int x=1; t++; if(!(tok=strtok(NULL, "\t\n"))) break; if (t==x++) { if(status) *status=*tok; } else if(t==x++) { if(status && (*status==STATUS_IDLE || *status==STATUS_SERVER_CRASHED || *status==STATUS_CLIENT_CRASHED)) { int b=0; if(backups) { // Build a list of backups. do { if(add_to_backup_list(backups, &b, tok)) { free(copy); return -1; } } while((tok=strtok(NULL, "\t\n"))); } } else { if(phase) *phase=*tok; } } else if(t==x++) { extract_ul(tok, &(cntr->total), &(cntr->total_changed), &(cntr->total_same), &(cntr->total_deleted), &(p1cntr->total)); } else if(t==x++) { extract_ul(tok, &(cntr->file), &(cntr->file_changed), &(cntr->file_same), &(cntr->file_deleted), &(p1cntr->file)); } else if(t==x++) { extract_ul(tok, &(cntr->enc), &(cntr->enc_changed), &(cntr->enc_same), &(cntr->enc_deleted), &(p1cntr->enc)); } else if(t==x++) { extract_ul(tok, &(cntr->meta), &(cntr->meta_changed), &(cntr->meta_same), &(cntr->meta_deleted), &(p1cntr->meta)); } else if(t==x++) { extract_ul(tok, &(cntr->encmeta), &(cntr->encmeta_changed), &(cntr->encmeta_same), &(cntr->encmeta_deleted), &(p1cntr->encmeta)); } else if(t==x++) { extract_ul(tok, &(cntr->dir), &(cntr->dir_changed), &(cntr->dir_same), &(cntr->dir_deleted), &(p1cntr->dir)); } else if(t==x++) { extract_ul(tok, &(cntr->slink), &(cntr->slink_changed), &(cntr->slink_same), &(cntr->slink_deleted), &(p1cntr->slink)); } else if(t==x++) { extract_ul(tok, &(cntr->hlink), &(cntr->hlink_changed), &(cntr->hlink_same), &(cntr->hlink_deleted), &(p1cntr->hlink)); } else if(t==x++) { extract_ul(tok, &(cntr->special), &(cntr->special_changed), &(cntr->special_same), &(cntr->special_deleted), &(p1cntr->special)); } else if(*counter_version==COUNTER_VERSION_2 && t==x++) { extract_ul(tok, &(cntr->vss), &(cntr->vss_changed), &(cntr->vss_same), &(cntr->vss_deleted), &(p1cntr->vss)); } else if(*counter_version==COUNTER_VERSION_2 && t==x++) { extract_ul(tok, &(cntr->encvss), &(cntr->encvss_changed), &(cntr->encvss_same), &(cntr->encvss_deleted), &(p1cntr->encvss)); } else if(*counter_version==COUNTER_VERSION_2 && t==x++) { extract_ul(tok, &(cntr->vss_t), &(cntr->vss_t_changed), &(cntr->vss_t_same), &(cntr->vss_t_deleted), &(p1cntr->vss_t)); } else if(*counter_version==COUNTER_VERSION_2 && t==x++) { extract_ul(tok, &(cntr->encvss_t), &(cntr->encvss_t_changed), &(cntr->encvss_t_same), &(cntr->encvss_t_deleted), &(p1cntr->encvss_t)); } else if(t==x++) { extract_ul(tok, &(cntr->gtotal), &(cntr->gtotal_changed), &(cntr->gtotal_same), &(cntr->gtotal_deleted), &(p1cntr->gtotal)); } else if(t==x++) { cntr->warning= strtoull(tok, NULL, 10); } else if(t==x++) { p1cntr->byte= strtoull(tok, NULL, 10); } else if(t==x++) { cntr->byte= strtoull(tok, NULL, 10); } else if(t==x++) { cntr->recvbyte= strtoull(tok, NULL, 10); } else if(t==x++) { cntr->sentbyte= strtoull(tok, NULL, 10); } else if(t==x++) { p1cntr->start=atol(tok); } else if(t==x++) { if(path && !(*path=strdup(tok))) { log_out_of_memory(__FUNCTION__); return -1; } } } } } free(copy); return 0; }
int recursive_delete(const char *d, const char *file, bool delfiles) { int n=-1; int ret=RECDEL_OK; struct dirent **dir; struct stat statp; char *directory=NULL; if(!file) { if(!(directory=prepend_s(d, "", 0))) return RECDEL_ERROR; } else if(!(directory=prepend_s(d, file, strlen(file)))) { log_out_of_memory(__FUNCTION__); return RECDEL_ERROR; } if(lstat(directory, &statp)) { // path does not exist. free(directory); return RECDEL_OK; } if((n=scandir(directory, &dir, 0, 0))<0) { logp("scandir %s: %s\n", directory, strerror(errno)); free(directory); return RECDEL_ERROR; } while(n--) { char *fullpath=NULL; if(dir[n]->d_ino==0 || !strcmp(dir[n]->d_name, ".") || !strcmp(dir[n]->d_name, "..")) { free(dir[n]); continue; } if(!(fullpath=prepend_s(directory, dir[n]->d_name, strlen(dir[n]->d_name)))) break; if(is_dir(fullpath)) { int r; if((r=recursive_delete(directory, dir[n]->d_name, delfiles))==RECDEL_ERROR) { free(fullpath); break; } // do not overwrite ret with OK if it previously // had ENTRIES_REMAINING if(r==RECDEL_ENTRIES_REMAINING) ret=r; } else if(delfiles) { if(unlink(fullpath)) { logp("unlink %s: %s\n", fullpath, strerror(errno)); ret=RECDEL_ENTRIES_REMAINING; } } else { ret=RECDEL_ENTRIES_REMAINING; } free(fullpath); free(dir[n]); } if(n>0) { ret=RECDEL_ERROR; for(; n>0; n--) free(dir[n]); } free(dir); if(ret==RECDEL_OK && rmdir(directory)) { logp("rmdir %s: %s\n", directory, strerror(errno)); ret=RECDEL_ERROR; } free(directory); return ret; }
int main (int argc, char *argv[]) { int ret=1; int option=0; int daemon=1; int forking=1; int strip=0; struct lock *lock=NULL; struct conf *conf=NULL; int forceoverwrite=0; enum action act=ACTION_LIST; const char *backup=NULL; const char *restoreprefix=NULL; const char *regex=NULL; const char *browsefile=NULL; const char *browsedir=NULL; const char *conffile=get_conf_path(); const char *orig_client=NULL; // The orig_client is the original client that the normal client // would like to restore from. #ifndef HAVE_WIN32 const char *sclient=NULL; // Status monitor client to view. int generate_ca_only=0; #endif int vss_restore=1; int json=0; init_log(argv[0]); while((option=getopt(argc, argv, "a:b:c:C:d:ghfFil:nr:s:vxjz:?"))!=-1) { switch(option) { case 'a': if(parse_action(&act, optarg)) goto end; break; case 'b': backup=optarg; break; case 'c': conffile=optarg; break; case 'C': orig_client=optarg; #ifndef HAVE_WIN32 sclient=optarg; #endif break; case 'd': restoreprefix=optarg; // for restores browsedir=optarg; // for lists break; case 'f': forceoverwrite=1; break; case 'F': daemon=0; break; case 'g': #ifndef HAVE_WIN32 generate_ca_only=1; #endif break; case 'i': cmd_print_all(); ret=0; goto end; case 'l': logp("-l <logfile> option obsoleted\n"); break; case 'n': forking=0; break; case 'r': regex=optarg; break; case 's': strip=atoi(optarg); break; case 'v': printf("%s-%s\n", progname(), VERSION); ret=0; goto end; case 'x': vss_restore=0; break; case 'j': json=1; break; case 'z': browsefile=optarg; break; case 'h': case '?': default: usage(); goto end; } } if(optind<argc) { usage(); goto end; } if(!(conf=conf_alloc())) goto end; if(reload(conf, conffile, 1 /* first time */, 0 /* no oldmax_children setting */, 0 /* no oldmax_status_children setting */, json)) goto end; if((act==ACTION_RESTORE || act==ACTION_VERIFY) && !backup) { logp("No backup specified. Using the most recent.\n"); backup="0"; } if(act==ACTION_DELETE && !backup) { logp("No backup specified for deletion.\n"); goto end; } if(conf->mode==MODE_CLIENT) { if(orig_client && *orig_client) { if(!(conf->orig_client=strdup(orig_client))) { log_out_of_memory(__func__); goto end; } } } if(conf->mode==MODE_SERVER && (act==ACTION_STATUS || act==ACTION_STATUS_SNAPSHOT || act==ACTION_CHAMP_CHOOSER)) { // These server modes need to run without getting the lock. } else { if(!(lock=lock_alloc_and_init(conf->lockfile))) goto end; lock_get(lock); switch(lock->status) { case GET_LOCK_GOT: break; case GET_LOCK_NOT_GOT: logp("Could not get lockfile.\n"); logp("Another process is probably running,\n"); goto end; case GET_LOCK_ERROR: default: logp("Could not get lockfile.\n"); logp("Maybe you do not have permissions to write to %s.\n", conf->lockfile); goto end; } } conf->overwrite=forceoverwrite; conf->strip=strip; conf->forking=forking; conf->daemon=daemon; if(replace_conf_str(backup, &conf->backup) || replace_conf_str(restoreprefix, &conf->restoreprefix) || replace_conf_str(regex, &conf->regex) || replace_conf_str(browsefile, &conf->browsefile) || replace_conf_str(browsedir, &conf->browsedir)) goto end; if(conf->mode==MODE_SERVER) { #ifdef HAVE_WIN32 logp("Sorry, server mode is not implemented for Windows.\n"); #else if(act==ACTION_STATUS || act==ACTION_STATUS_SNAPSHOT) { // We are running on the server machine, being a client // of the burp server, getting status information. ret=status_client_ncurses(conf, act, sclient); } else if(act==ACTION_CHAMP_CHOOSER) { // We are running on the server machine, wanting to // be a standalone champion chooser process. if(!sclient || !*sclient) { logp("No client name specified for standalone champion chooser process.\n"); logp("Try using the '-C' option.\n"); ret=1; } else ret=champ_chooser_server_standalone(conf, sclient); } else ret=server(conf, conffile, lock, generate_ca_only); #endif } else { logp("before client\n"); ret=client(conf, act, vss_restore, json); logp("after client\n"); } end: lock_release(lock); lock_free(&lock); conf_free(conf); return ret; }
static int do_recursive_delete(const char *d, const char *file, uint8_t delfiles, int32_t name_max) { int ret=RECDEL_ERROR; DIR *dirp=NULL; struct dirent *entry=NULL; struct dirent *result; struct stat statp; char *directory=NULL; char *fullpath=NULL; if(!file) { if(!(directory=prepend_s(d, ""))) goto end; } else if(!(directory=prepend_s(d, file))) { log_out_of_memory(__func__); goto end; } if(lstat(directory, &statp)) { // path does not exist. ret=RECDEL_OK; goto end; } if(!(dirp=opendir(directory))) { logp("opendir %s: %s\n", directory, strerror(errno)); goto end; } if(!(entry=(struct dirent *) malloc_w(sizeof(struct dirent)+name_max+100, __func__))) goto end; while(1) { if(readdir_r(dirp, entry, &result) || !result) { // Got to the end of the directory. ret=RECDEL_OK; break; } if(!entry->d_ino || !strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue; free_w(&fullpath); if(!(fullpath=prepend_s(directory, entry->d_name))) goto end; if(is_dir(fullpath, entry)>0) { int r; if((r=do_recursive_delete(directory, entry->d_name, delfiles, name_max))==RECDEL_ERROR) goto end; // do not overwrite ret with OK if it previously // had ENTRIES_REMAINING if(r==RECDEL_ENTRIES_REMAINING) ret=r; } else if(delfiles) { if(unlink(fullpath)) { logp("unlink %s: %s\n", fullpath, strerror(errno)); ret=RECDEL_ENTRIES_REMAINING; } } else { ret=RECDEL_ENTRIES_REMAINING; } } if(ret==RECDEL_OK && rmdir(directory)) { logp("rmdir %s: %s\n", directory, strerror(errno)); ret=RECDEL_ERROR; } end: if(dirp) closedir(dirp); free_w(&fullpath); free_w(&directory); free_v((void **)&entry); return ret; }
int run_bedup(int argc, char *argv[]) { int i=1; int ret=0; int option=0; int nonburp=0; unsigned int maxlinks=DEF_MAX_LINKS; char *groups=NULL; char ext[16]=""; int givenconfigfile=0; const char *configfile=NULL; configfile=get_config_path(); snprintf(ext, sizeof(ext), ".bedup.%d", getpid()); while((option=getopt(argc, argv, "c:dg:hlm:nvV?"))!=-1) { switch(option) { case 'c': configfile=optarg; givenconfigfile=1; break; case 'd': deletedups=1; break; case 'g': groups=optarg; break; case 'l': makelinks=1; break; case 'm': maxlinks=atoi(optarg); break; case 'n': nonburp=1; break; case 'V': printf("%s-%s\n", prog, VERSION); return 0; case 'v': verbose=1; break; case 'h': case '?': return usage(); } } if(nonburp && givenconfigfile) { logp("-n and -c options are mutually exclusive\n"); return 1; } if(nonburp && groups) { logp("-n and -g options are mutually exclusive\n"); return 1; } if(!nonburp && maxlinks!=DEF_MAX_LINKS) { logp("-m option is specified via the configuration file in burp mode (max_hardlinks=)\n"); return 1; } if(deletedups && makelinks) { logp("-d and -l options are mutually exclusive\n"); return 1; } if(deletedups && !nonburp) { logp("-d option requires -n option\n"); return 1; } if(optind>=argc) { if(nonburp) { logp("No directories found after options\n"); return 1; } } else { if(!nonburp) { logp("Do not specify extra arguments.\n"); return 1; } } if(maxlinks<2) { logp("The argument to -m needs to be greater than 1.\n"); return 1; } if(nonburp) { // Read directories from command line. for(i=optind; i<argc; i++) { // Strip trailing slashes, for tidiness. if(argv[i][strlen(argv[i])-1]=='/') argv[i][strlen(argv[i])-1]='\0'; if(process_dir("", argv[i], ext, maxlinks, 0 /* not burp mode */, 0 /* level */)) { ret=1; break; } } } else { struct conf **globalcs=NULL; struct strlist *grouplist=NULL; struct lock *globallock=NULL; if(groups) { char *tok=NULL; if((tok=strtok(groups, ",\n"))) { do { if(strlist_add(&grouplist, tok, 1)) { log_out_of_memory(__func__); return -1; } } while((tok=strtok(NULL, ",\n"))); } if(!grouplist) { logp("unable to read list of groups\n"); return -1; } } // Read directories from config files, and get locks. if(!(globalcs=confs_alloc())) return -1; if(confs_init(globalcs)) return -1; if(conf_load_global_only(configfile, globalcs)) return 1; if(get_e_burp_mode(globalcs[OPT_BURP_MODE])!=BURP_MODE_SERVER) { logp("%s is not a server config file\n", configfile); confs_free(&globalcs); return 1; } logp("Dedup clients from %s\n", get_string(globalcs[OPT_CLIENTCONFDIR])); maxlinks=get_int(globalcs[OPT_MAX_HARDLINKS]); if(grouplist) { struct strlist *g=NULL; logp("in dedup groups:\n"); for(g=grouplist; g; g=g->next) logp("%s\n", g->path); } else { char *lockpath=NULL; // Only get the global lock when doing a global run. // If you are doing individual groups, you are likely // to want to do many different dedup jobs and a // global lock would get in the way. if(!(lockpath=prepend( get_string(globalcs[OPT_LOCKFILE]), ".bedup", "")) || !(globallock=lock_alloc_and_init(lockpath))) return 1; lock_get(globallock); if(globallock->status!=GET_LOCK_GOT) { logp("Could not get lock %s (%d)\n", lockpath, globallock->status); free_w(&lockpath); return 1; } logp("Got %s\n", lockpath); } ret=iterate_over_clients(globalcs, grouplist, ext, maxlinks); confs_free(&globalcs); lock_release(globallock); lock_free(&globallock); strlists_free(&grouplist); } if(!nonburp) { logp("%d client storages scanned\n", ccount); } logp("%llu duplicate %s found\n", count, count==1?"file":"files"); logp("%llu bytes %s%s\n", savedbytes, (makelinks || deletedups)?"saved":"saveable", bytes_to_human(savedbytes)); return ret; }
static int jiggle(struct sdirs *sdirs, struct fdirs *fdirs, struct sbuf *sb, int hardlinked_current, const char *deltabdir, const char *deltafdir, const char *sigpath, FILE **delfp, struct conf *cconf) { int ret=-1; struct stat statp; char *oldpath=NULL; char *newpath=NULL; char *finpath=NULL; char *deltafpath=NULL; const char *datapth=sb->burp1->datapth.buf; // If the previous backup was a hardlinked_archive, there will not be // a currentdup directory - just directly use the file in the previous // backup. if(!(oldpath=prepend_s(hardlinked_current? sdirs->currentdata:fdirs->currentdupdata, datapth)) || !(newpath=prepend_s(fdirs->datadirtmp, datapth)) || !(finpath=prepend_s(fdirs->datadir, datapth)) || !(deltafpath=prepend_s(deltafdir, datapth))) goto end; if(!lstat(finpath, &statp) && S_ISREG(statp.st_mode)) { // Looks like an interrupted jiggle // did this file already. static int donemsg=0; if(!lstat(deltafpath, &statp) && S_ISREG(statp.st_mode)) { logp("deleting unneeded forward delta: %s\n", deltafpath); unlink(deltafpath); } if(!donemsg) { logp("skipping already present file: %s\n", finpath); logp("to save log space, skips of other already present files will not be logged\n"); donemsg++; } } else if(mkpath(&finpath, fdirs->datadir)) { logp("could not create path for: %s\n", finpath); goto end; } else if(mkpath(&newpath, fdirs->datadirtmp)) { logp("could not create path for: %s\n", newpath); goto end; } else if(!lstat(deltafpath, &statp) && S_ISREG(statp.st_mode)) { int lrs; char *infpath=NULL; // Got a forward patch to do. // First, need to gunzip the old file, // otherwise the librsync patch will take // forever, because it will be doing seeks // all over the place, and gzseeks are slow. if(!(infpath=prepend_s(deltafdir, "inflate"))) { log_out_of_memory(__func__); goto end; } //logp("Fixing up: %s\n", datapth); if(inflate_or_link_oldfile(oldpath, infpath, sb->compression, cconf)) { logp("error when inflating old file: %s\n", oldpath); free(infpath); goto end; } if((lrs=do_patch(NULL, infpath, deltafpath, newpath, cconf->compression, sb->compression /* from the manifest */, cconf))) { logp("WARNING: librsync error when patching %s: %d\n", oldpath, lrs); cntr_add(cconf->cntr, CMD_WARNING, 1); // Try to carry on with the rest of the backup // regardless. //ret=-1; // Remove anything that got written. unlink(newpath); unlink(infpath); free(infpath); // First, note that we want to remove this entry from // the manifest. if(!*delfp && !(*delfp=open_file(fdirs->deletionsfile, "ab"))) { // Could not mark this file as deleted. Fatal. goto end; } if(sbufl_to_manifest(sb, *delfp, NULL)) goto end; if(fflush(*delfp)) { logp("error fflushing deletions file in %s: %s\n", __func__, strerror(errno)); goto end; } ret=0; goto end; } // Get rid of the inflated old file. unlink(infpath); free(infpath); // Need to generate a reverse diff, unless we are keeping a // hardlinked archive. if(!hardlinked_current) { if(gen_rev_delta(sigpath, deltabdir, oldpath, newpath, datapth, sb, cconf)) goto end; } // Power interruptions should be recoverable. If it happens // before this point, the data jiggle for this file has to be // done again. // Once finpath is in place, no more jiggle is required. // Use the fresh new file. // Rename race condition is of no consequence, because finpath // will just get recreated automatically. if(do_rename(newpath, finpath)) goto end; // Remove the forward delta, as it is no longer needed. There // is a reverse diff and the finished finished file is in place. //logp("Deleting delta.forward...\n"); unlink(deltafpath); // Remove the old file. If a power cut happens just before // this, the old file will hang around forever. // FIX THIS: maybe put in something to detect this. // ie, both a reverse delta and the old file exist. if(!hardlinked_current) { //logp("Deleting oldpath...\n"); unlink(oldpath); } } else if(!lstat(newpath, &statp) && S_ISREG(statp.st_mode)) { // Use the fresh new file. // This needs to happen after checking // for the forward delta, because the // patching stuff writes to newpath. // Rename race condition is of no consequence, because finpath // will just get recreated automatically. //logp("Using newly received file\n"); if(do_rename(newpath, finpath)) goto end; } else if(!lstat(oldpath, &statp) && S_ISREG(statp.st_mode)) { // Use the old unchanged file. // Hard link it first. //logp("Hard linking to old file: %s\n", datapth); if(do_link(oldpath, finpath, &statp, cconf, 0 /* do not overwrite finpath (should never need to) */)) goto end; else { // If we are not keeping a hardlinked // archive, delete the old link. if(!hardlinked_current) { //logp("Unlinking old file: %s\n", oldpath); unlink(oldpath); } } } else { logp("could not find: %s\n", oldpath); goto end; } ret=0; end: free_w(&oldpath); free_w(&newpath); free_w(&finpath); free_w(&deltafpath); return ret; }
/* Need to make all the stuff that this does atomic so that existing backups never get broken, even if somebody turns the power off on the server. */ static int atomic_data_jiggle(struct sdirs *sdirs, struct fdirs *fdirs, int hardlinked_current, struct conf *cconf, unsigned long bno) { int ars=0; int ret=-1; char *datapth=NULL; char *tmpman=NULL; struct stat statp; char *deltabdir=NULL; char *deltafdir=NULL; char *sigpath=NULL; gzFile zp=NULL; struct sbuf *sb=NULL; FILE *delfp=NULL; logp("Doing the atomic data jiggle...\n"); if(!(tmpman=get_tmp_filename(fdirs->manifest))) goto end; if(lstat(fdirs->manifest, &statp)) { // Manifest does not exist - maybe the server was killed before // it could be renamed. logp("%s did not exist - trying %s\n", fdirs->manifest, tmpman); // Rename race condition is of no consequence, because manifest // already does not exist. do_rename(tmpman, fdirs->manifest); } if(!(zp=gzopen_file(fdirs->manifest, "rb"))) goto end; if(!(deltabdir=prepend_s(fdirs->currentdup, "deltas.reverse")) || !(deltafdir=prepend_s(sdirs->finishing, "deltas.forward")) || !(sigpath=prepend_s(fdirs->currentdup, "sig.tmp")) || !(sb=sbuf_alloc(cconf))) { log_out_of_memory(__func__); goto end; } mkdir(fdirs->datadir, 0777); while(!(ars=sbufl_fill(sb, NULL, NULL, zp, cconf->cntr))) { if(sb->burp1->datapth.buf) { if(write_status(STATUS_SHUFFLING, sb->burp1->datapth.buf, cconf)) goto end; if((ret=jiggle(sdirs, fdirs, sb, hardlinked_current, deltabdir, deltafdir, sigpath, &delfp, cconf))) goto end; } sbuf_free_content(sb); } if(ars<0) goto end; if(close_fp(&delfp)) { logp("error closing %s in atomic_data_jiggle\n", fdirs->deletionsfile); goto end; } if(maybe_delete_files_from_manifest(tmpman, fdirs, cconf)) goto end; // Remove the temporary data directory, we have probably removed // useful files from it. sync(); // try to help CIFS recursive_delete(deltafdir, NULL, 0 /* do not del files */); end: gzclose_fp(&zp); close_fp(&delfp); sbuf_free(&sb); free_w(&deltabdir); free_w(&deltafdir); free_w(&sigpath); free_w(&datapth); free_w(&tmpman); return ret; }