static int parse_client_data(struct asfd *srfd, struct cstat *clist, struct conf **confs) { int ret=0; char *command=NULL; char *client=NULL; char *backup=NULL; char *logfile=NULL; char *browse=NULL; const char *cp=NULL; struct cstat *cstat=NULL; struct bu *bu=NULL; //printf("got client data: '%s'\n", srfd->rbuf->buf); cp=srfd->rbuf->buf; // The client monitor will send an initial blank line to kick things // off. Until it is sent, we will not even have entered the code // in this file. So, without it, data from the parent will not have // been read, and the monitor client will be given incomplete // information for its first response. // Just ignore the new line at this point, it has served its purpose. if(srfd->rbuf->len==1 && !strcmp(cp, "\n")) { ret=0; goto end; } command=get_str(&cp, "j:", 0); client=get_str(&cp, "c:", 0); backup=get_str(&cp, "b:", 0); logfile=get_str(&cp, "l:", 0); browse=get_str(&cp, "p:", 1); if(command) { if(!strcmp(command, "pretty-print-on")) { json_set_pretty_print(1); if(json_send_warn(srfd, "Pretty print on")) goto error; } else if(!strcmp(command, "pretty-print-off")) { json_set_pretty_print(0); if(json_send_warn(srfd, "Pretty print off")) goto error; } else { if(json_send_warn(srfd, "Unknown command")) goto error; } goto end; } if(browse) { free_w(&logfile); if(!(logfile=strdup_w("manifest", __func__))) goto error; strip_trailing_slashes(&browse); } //dump_cbno(clist, "pcd"); if(client && *client) { if(!(cstat=cstat_get_by_name(clist, client))) { if(json_send_warn(srfd, "Could not find client")) goto error; goto end; } if(cstat_set_backup_list(cstat)) { if(json_send_warn(srfd, "Could not get backup list")) goto error; goto end; } } if(cstat && backup) { unsigned long bno=0; if(!(bno=strtoul(backup, NULL, 10))) { if(json_send_warn(srfd, "Could not get backup number")) goto error; goto end; } for(bu=cstat->bu; bu; bu=bu->prev) if(bu->bno==bno) break; if(!bu) { if(json_send_warn(srfd, "Backup not found")) goto error; goto end; } } if(logfile) { if(strcmp(logfile, "manifest") && strcmp(logfile, "backup") && strcmp(logfile, "restore") && strcmp(logfile, "verify") && strcmp(logfile, "backup_stats") && strcmp(logfile, "restore_stats") && strcmp(logfile, "verify_stats")) { if(json_send_warn(srfd, "File not supported")) goto error; goto end; } } /* printf("client: %s\n", client?:""); printf("backup: %s\n", backup?:""); printf("logfile: %s\n", logfile?:""); */ if(cstat) { if(!cstat->run_status) cstat_set_run_status(cstat); } else for(cstat=clist; cstat; cstat=cstat->next) { if(!cstat->run_status) cstat_set_run_status(cstat); } if(json_send(srfd, clist, cstat, bu, logfile, browse, get_int(confs[OPT_MONITOR_BROWSE_CACHE]))) goto error; goto end; error: ret=-1; end: free_w(&client); free_w(&backup); free_w(&logfile); free_w(&browse); 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==0 || !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)) { 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; }
void rs_filebuf_free(rs_filebuf_t **fb) { if(!fb || !*fb) return; free_w(&((*fb)->buf)); free_v((void **)fb); }
void blk_free_content(struct blk *blk) { if(!blk) return; free_w(&blk->data); }
int do_restore_client(struct asfd *asfd, struct conf **confs, enum action act, int vss_restore) { int ret=-1; char msg[512]=""; struct sbuf *sb=NULL; struct blk *blk=NULL; BFILE *bfd=NULL; char *fullpath=NULL; char *style=NULL; char *datpath=NULL; struct cntr *cntr=get_cntr(confs); enum protocol protocol=get_protocol(confs); int strip=get_int(confs[OPT_STRIP]); int overwrite=get_int(confs[OPT_OVERWRITE]); const char *backup=get_string(confs[OPT_BACKUP]); const char *regex=get_string(confs[OPT_REGEX]); const char *restore_prefix=get_string(confs[OPT_RESTOREPREFIX]); const char *encryption_password=get_string(confs[OPT_ENCRYPTION_PASSWORD]); if(!(bfd=bfile_alloc())) goto end; bfile_init(bfd, 0, cntr); snprintf(msg, sizeof(msg), "%s %s:%s", act_str(act), backup?backup:"", regex?regex:""); logp("doing %s\n", msg); if(asfd->write_str(asfd, CMD_GEN, msg) || asfd_read_expect(asfd, CMD_GEN, "ok")) goto error; logp("doing %s confirmed\n", act_str(act)); #if defined(HAVE_WIN32) if(act==ACTION_RESTORE) win32_enable_backup_privileges(); #endif if(!(style=get_restore_style(asfd, confs))) goto error; if(!strcmp(style, RESTORE_SPOOL)) { if(restore_spool(asfd, confs, &datpath)) goto error; } else logp("Streaming restore direct\n"); logf("\n"); if(get_int(confs[OPT_SEND_CLIENT_CNTR]) && cntr_recv(asfd, confs)) goto error; if(!(sb=sbuf_alloc(protocol)) || (protocol==PROTO_2 && !(blk=blk_alloc()))) { log_and_send_oom(asfd, __func__); goto error; } while(1) { sbuf_free_content(sb); if(protocol==PROTO_1) sb->flags |= SBUF_CLIENT_RESTORE_HACK; switch(sbuf_fill_from_net(sb, asfd, blk, datpath, cntr)) { case 0: break; case 1: if(asfd->write_str(asfd, CMD_GEN, "restoreend ok")) goto error; goto end; // It was OK. default: case -1: goto error; } if(protocol==PROTO_2) { if(blk->data) { int wret=0; if(act==ACTION_VERIFY) cntr_add(cntr, CMD_DATA, 1); else wret=write_data(asfd, bfd, blk); if(!datpath) blk_free_content(blk); blk->data=NULL; if(wret) goto error; continue; } else if(sb->endfile.buf) { continue; } } switch(sb->path.cmd) { case CMD_DIRECTORY: case CMD_FILE: case CMD_ENC_FILE: case CMD_SOFT_LINK: case CMD_HARD_LINK: case CMD_SPECIAL: case CMD_METADATA: case CMD_ENC_METADATA: case CMD_VSS: case CMD_ENC_VSS: case CMD_VSS_T: case CMD_ENC_VSS_T: case CMD_EFS_FILE: if(strip) { int s; s=strip_path_components(asfd, sb, strip, cntr, protocol); if(s<0) goto error; if(s==0) { // Too many components stripped // - carry on. continue; } // It is OK, sb.path is now stripped. } free_w(&fullpath); if(!(fullpath=prepend_s(restore_prefix, sb->path.buf))) { log_and_send_oom(asfd, __func__); goto error; } if(act==ACTION_RESTORE) { strip_invalid_characters(&fullpath); if(!overwrite_ok(sb, overwrite, #ifdef HAVE_WIN32 bfd, #endif fullpath)) { char msg[512]=""; // Something exists at that path. snprintf(msg, sizeof(msg), "Path exists: %s\n", fullpath); if(restore_interrupt(asfd, sb, msg, cntr, protocol)) goto error; continue; } } break; case CMD_MESSAGE: case CMD_WARNING: log_recvd(&sb->path, cntr, 1); logf("\n"); continue; default: break; } switch(sb->path.cmd) { // These are the same in both protocol1 and protocol2. case CMD_DIRECTORY: if(restore_dir(asfd, sb, fullpath, act, cntr, protocol)) goto error; continue; case CMD_SOFT_LINK: case CMD_HARD_LINK: if(restore_link(asfd, sb, fullpath, act, cntr, protocol, restore_prefix)) goto error; continue; case CMD_SPECIAL: if(restore_special(asfd, sb, fullpath, act, cntr, protocol)) goto error; continue; default: break; } if(protocol==PROTO_2) { if(restore_switch_protocol2(asfd, sb, fullpath, act, bfd, vss_restore, cntr)) goto error; } else { if(restore_switch_protocol1(asfd, sb, fullpath, act, bfd, vss_restore, cntr, encryption_password)) goto error; } } end: ret=0; error: // It is possible for a fd to still be open. bfd->close(bfd, asfd); bfile_free(&bfd); cntr_print_end(cntr); cntr_print(cntr, act); if(!ret) logp("%s finished\n", act_str(act)); else logp("ret: %d\n", ret); sbuf_free(&sb); free_w(&style); if(datpath) { recursive_delete(datpath); free_w(&datpath); } free_w(&fullpath); blk_free(&blk); return ret; }
void iobuf_free_content(struct iobuf *iobuf) { if(!iobuf) return; free_w(&iobuf->buf); iobuf_init(iobuf); }
static int recover_working(struct async *as, struct sdirs *sdirs, const char *incexc, int *resume, struct conf **cconfs) { int ret=-1; char msg[256]=""; char *logpath=NULL; struct stat statp; char *phase1datatmp=NULL; enum recovery_method recovery_method=get_e_recovery_method( cconfs[OPT_WORKING_DIR_RECOVERY_METHOD]); // The working directory has not finished being populated. // Check what to do. if(get_fullrealwork(as->asfd, sdirs, cconfs)) goto end; if(!sdirs->rworking) goto end; log_recovery_method(sdirs, recovery_method); if(!(phase1datatmp=get_tmp_filename(sdirs->phase1data))) goto end; // If there is still a phase1 tmp file... if(!lstat(phase1datatmp, &statp) || // ...or phase1 has not even got underway yet... (lstat(phase1datatmp, &statp) && lstat(sdirs->phase1data, &statp) && lstat(sdirs->changed, &statp) && lstat(sdirs->unchanged, &statp))) { // ...phase 1 did not complete - delete everything. logp("Phase 1 has not completed.\n"); recovery_method=RECOVERY_METHOD_DELETE; } if(recovery_method==RECOVERY_METHOD_DELETE) { ret=working_delete(as, sdirs, cconfs); goto end; } // We are not deleting the old working directory - open the log inside // for appending. if(!(logpath=prepend_s(sdirs->rworking, "log")) || log_fzp_set(logpath, cconfs)) goto end; switch(recovery_method) { case RECOVERY_METHOD_DELETE: // Dealt with above. break; case RECOVERY_METHOD_RESUME: ret=working_resume(as, sdirs, incexc, resume, cconfs); break; case RECOVERY_METHOD_UNSET: default: snprintf(msg, sizeof(msg), "Unknown working_dir_recovery_method: %d\n", (int)recovery_method); log_and_send(as->asfd, msg); break; } end: free_w(&logpath); free_w(&phase1datatmp); log_fzp_set(NULL, cconfs); // fclose the logfzp return ret; }
static #endif int status_client_ncurses_main_loop(struct async *as, struct asfd *so_asfd, struct sel *sel, const char *orig_client) { int ret=-1; char *client=NULL; int count=0; struct asfd *asfd=NULL; struct asfd *sfd=NULL; // Server asfd. int reqdone=0; if(!sel || !as || !(stdout_asfd=so_asfd) || !(sfd=as->asfd)) { logp("parameters not set up correctly in %s\n", __func__); goto error; } sel->page=PAGE_CLIENT_LIST; if(orig_client) { client=strdup_w(orig_client, __func__); sel->page=PAGE_BACKUP_LIST; } if(json_input_init()) goto end; while(1) { if(need_status(sel) && !reqdone) { char *req=NULL; if(sel->page>PAGE_CLIENT_LIST) { if(client) req=client; else if(sel->client) req=sel->client->name; } if(request_status(sfd, req, sel)) goto error; // We only want to start on the client the user gave to // us. Freeing it will allow the user to browse other // clients thereafter. free_w(&client); if(actg==ACTION_STATUS_SNAPSHOT) reqdone=1; } if(as->read_write(as)) { // FIX THIS - an exception is thrown when the console // is resized. /* if(sfd->want_to_remove) { sfd->want_to_remove=0; continue; } */ logp("Exiting main loop\n"); goto error; } for(asfd=as->asfd; asfd; asfd=asfd->next) { while(asfd->rbuf->buf) { switch(parse_data(asfd, sel, count)) { case 0: break; case 1: goto end; default: goto error; } iobuf_free_content(asfd->rbuf); if(asfd->parse_readbuf(asfd)) goto error; } // Select things if they are not already selected. if(sel->client) { if(!sel->backup) sel->backup=sel->client->bu; } else sel->client=sel->clist; } #ifdef HAVE_NCURSES if(actg==ACTION_STATUS && update_screen(sel)) goto error; refresh(); #endif if(actg==ACTION_STATUS_SNAPSHOT && sel->client) { if(update_screen(sel)) goto error; stdout_asfd->write_str(stdout_asfd, CMD_GEN, "\n"); break; } } end: ret=0; error: json_input_free(); return ret; }
static int process_entry(struct strlist *ig, struct conf **confs) { int ret=-1; size_t len1=0; char *sav=NULL; char **splitstr1=NULL; WIN32_FIND_DATA ffd; HANDLE hFind=INVALID_HANDLE_VALUE; convert_backslashes(&ig->path); if(ig->path[strlen(ig->path)-1]!='*') { if(!(splitstr1=xstrsplit(ig->path, "*", &len1))) goto end; } if(len1>2) { logp("include_glob error: '%s' contains at least" " two '*' which is not currently supported\n", ig->path); goto end; } if(len1>1) { char *tmppath=NULL; if(astrcat(&tmppath, splitstr1[0], __func__) || !(sav=strdup_w(tmppath, __func__)) || astrcat(&tmppath, "*", __func__)) goto end; hFind=FindFirstFileA(tmppath, &ffd); free_w(&tmppath); } else hFind=FindFirstFileA(ig->path, &ffd); if(hFind==INVALID_HANDLE_VALUE) { LPVOID lpMsgBuf; DWORD dw=GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL ); logp("Error: %s\n", lpMsgBuf); LocalFree(lpMsgBuf); goto end; } do { if(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && strcmp(ffd.cFileName, ".") && strcmp(ffd.cFileName, "..")) { char *tmppath=NULL; if(len1<2) { if(ig->path[strlen(ig->path)-1]=='*') { if(!(tmppath=xstrsub(ig->path, 0, strlen(ig->path)-1)) || astrcat(&tmppath, ffd.cFileName, __func__)) goto end; } else if(!(tmppath=strdup_w(ig->path, __func__))) goto end; } else { if(astrcat(&tmppath, sav, __func__) || astrcat(&tmppath, ffd.cFileName, __func__) || astrcat(&tmppath, splitstr1[1], __func__)) goto end; } if(add_to_strlist(confs[OPT_INCLUDE], tmppath, 1)) goto end; free_w(&tmppath); } } while(FindNextFileA(hFind, &ffd)!=0); FindClose(hFind); ret=0; end: if(splitstr1) { free_w(&sav); xfree_list(splitstr1, len1); } return ret; }
int backup_phase1_server_all(struct async *as, struct sdirs *sdirs, struct conf **confs) { int ret=-1; struct sbuf *sb=NULL; char *phase1tmp=NULL; struct asfd *asfd=as->asfd; struct manio *manio=NULL; enum protocol protocol=get_protocol(confs); struct cntr *cntr=get_cntr(confs); logp("Begin phase1 (file system scan)\n"); if(!(phase1tmp=get_tmp_filename(sdirs->phase1data)) || !(manio=manio_open_phase1(phase1tmp, comp_level(get_int(confs[OPT_COMPRESSION])), protocol)) || !(sb=sbuf_alloc(protocol))) goto error; while(1) { sbuf_free_content(sb); switch(sbuf_fill_from_net(sb, asfd, NULL, NULL, cntr)) { case 0: break; case 1: // Last thing the client sends is // 'backupphase2', and it wants an 'ok' reply. if(asfd->write_str(asfd, CMD_GEN, "ok") || send_msg_fzp(manio->fzp, CMD_GEN, "phase1end", strlen("phase1end"))) goto error; goto end; case -1: default: goto error; } if(write_status(CNTR_STATUS_SCANNING, sb->path.buf, cntr) || manio_write_sbuf(manio, sb)) goto error; cntr_add_phase1(cntr, sb->path.cmd, 0); if(sbuf_is_filedata(sb)) { cntr_add_val(cntr, CMD_BYTES_ESTIMATED, (uint64_t)sb->statp.st_size, 0); } } end: if(manio_close(&manio)) { logp("error closing %s in backup_phase1_server\n", phase1tmp); goto error; } if(check_quota(as, cntr, get_uint64_t(confs[OPT_HARD_QUOTA]), get_uint64_t(confs[OPT_SOFT_QUOTA]))) goto error; // Possible rename race condition is of no consequence here, because // the working directory will always get deleted if phase1 is not // complete. if(do_rename(phase1tmp, sdirs->phase1data)) goto error; //cntr_print(p1cntr, cntr, ACTION_BACKUP); logp("End phase1 (file system scan)\n"); ret=0; error: free_w(&phase1tmp); manio_close(&manio); sbuf_free(&sb); return ret; }
int authorise_server(struct asfd *asfd, struct conf **globalcs, struct conf **cconfs) { int ret=-1; char *cp=NULL; char *password=NULL; char *cname=NULL; char whoareyou[256]=""; struct iobuf *rbuf=asfd->rbuf; const char *peer_version=NULL; if(asfd->read(asfd)) { logp("unable to read initial message\n"); goto end; } if(rbuf->cmd!=CMD_GEN || strncmp_w(rbuf->buf, "hello")) { iobuf_log_unexpected(rbuf, __func__); goto end; } // String may look like... // "hello" // "hello:(version)" // (version) is a version number if((cp=strchr(rbuf->buf, ':'))) { cp++; if(cp && set_string(cconfs[OPT_PEER_VERSION], cp)) goto end; } iobuf_free_content(rbuf); snprintf(whoareyou, sizeof(whoareyou), "whoareyou"); peer_version=get_string(cconfs[OPT_PEER_VERSION]); if(peer_version) { long min_ver=0; long cli_ver=0; if((min_ver=version_to_long("1.3.2"))<0 || (cli_ver=version_to_long(peer_version))<0) return -1; // Stick the server version on the end of the whoareyou string. // if the client version is recent enough. if(min_ver<=cli_ver) snprintf(whoareyou, sizeof(whoareyou), "whoareyou:%s", PACKAGE_VERSION); } if(asfd->write_str(asfd, CMD_GEN, whoareyou) || asfd->read(asfd)) { logp("unable to get client name\n"); goto end; } if(!(cname=strdup_w(rbuf->buf, __func__))) goto end; if(!get_int(globalcs[OPT_CNAME_FQDN])) strip_fqdn(&cname); if(get_int(globalcs[OPT_CNAME_LOWERCASE])) strlwr(cname); if(set_string(cconfs[OPT_CNAME], cname)) goto end; iobuf_free_content(rbuf); if(asfd->write_str(asfd, CMD_GEN, "okpassword") || asfd->read(asfd)) { logp("unable to get password for client %s\n", get_string(cconfs[OPT_CNAME])); goto end; } password=rbuf->buf; iobuf_init(rbuf); if(check_client_and_password(globalcs, password, cconfs)) goto end; if(get_int(cconfs[OPT_VERSION_WARN])) version_warn(asfd, get_cntr(globalcs), cconfs); logp("auth ok for: %s%s\n", get_string(cconfs[OPT_CNAME]), get_int(cconfs[OPT_PASSWORD_CHECK])? "":" (no password needed)"); if(asfd->write_str(asfd, CMD_GEN, "ok")) goto end; ret=0; end: iobuf_free_content(rbuf); free_w(&password); free_w(&cname); return ret; }
void dpth_free(struct dpth **dpth) { if(!dpth || !*dpth) return; free_w(&((*dpth)->base_path)); free_v((void **)dpth); }
static int input_string(void *ctx, const unsigned char *val, size_t len) { char *str; if(!(str=(char *)malloc_w(len+2, __func__))) return 0; snprintf(str, len+1, "%s", val); if(in_counters) { if(!strcmp(lastkey, "name")) { // Ignore 'name' in a counters object. We use 'type' // instead. } else if(!strcmp(lastkey, "type")) { if(!current) goto error; cntr_ent=current->cntr->ent[(uint8_t)*str]; } else { goto error; } goto end; } else if(!strcmp(lastkey, "name")) { if(cnew) goto error; if(!(current=cstat_get_by_name(*cslist, str))) { if(!(cnew=cstat_alloc()) || cstat_init(cnew, str, NULL)) goto error; current=cnew; } goto end; } else if(!strcmp(lastkey, "run_status")) { if(!current) goto error; current->run_status=run_str_to_status(str); goto end; } else if(!strcmp(lastkey, "phase")) { if(!current) goto error; current->cntr->cntr_status=cntr_str_to_status(str); goto end; } else if(!strcmp(lastkey, "flags")) { if(!current) goto error; if(is_wrap(str, "hardlinked", BU_HARDLINKED) || is_wrap(str, "deletable", BU_DELETABLE) || is_wrap(str, "working", BU_WORKING) || is_wrap(str, "finishing", BU_FINISHING) || is_wrap(str, "current", BU_CURRENT) || is_wrap(str, "manifest", BU_MANIFEST)) goto end; } else if(!strcmp(lastkey, "counters")) // Do we need this? { goto end; } else if(!strcmp(lastkey, "list")) { if(is_wrap(str, "backup", BU_LOG_BACKUP) || is_wrap(str, "restore", BU_LOG_RESTORE) || is_wrap(str, "verify", BU_LOG_VERIFY) || is_wrap(str, "backup_stats", BU_STATS_BACKUP) || is_wrap(str, "restore_stats", BU_STATS_RESTORE) || is_wrap(str, "verify_stats", BU_STATS_VERIFY)) goto end; } else if(!strcmp(lastkey, "logs")) { goto end; } else if(!strcmp(lastkey, "logline")) { goto end; } else if(!strcmp(lastkey, "backup") || !strcmp(lastkey, "restore") || !strcmp(lastkey, "verify") || !strcmp(lastkey, "backup_stats") || !strcmp(lastkey, "restore_stats") || !strcmp(lastkey, "verify_stats")) { // Log file contents. if(lline_add(&ll_list, str)) goto error; goto end; } error: logp("Unexpected string: %s %s\n", lastkey, str); free_w(&str); return 0; end: free_w(&str); return 1; }
static void merge_bu_lists(void) { struct bu *n; struct bu *o; struct bu *lastn=NULL; struct bu *lasto=NULL; for(o=current->bu; o; ) { int found_in_new=0; lastn=NULL; for(n=bu_list; n; n=n->next) { if(o->bno==n->bno) { // Found o in new list. // Copy the fields from new to old. found_in_new=1; o->flags=n->flags; free_w(&o->timestamp); o->timestamp=n->timestamp; n->timestamp=NULL; // Remove it from new list. if(lastn) { lastn->next=n->next; if(n->next) n->next->prev=lastn; } else { bu_list=n->next; if(bu_list) bu_list->prev=NULL; } bu_free(&n); n=lastn; break; } lastn=n; } if(!found_in_new) { // Could not find o in new list. // Remove it from old list. if(lasto) { lasto->next=o->next; if(o->next) o->next->prev=lasto; } else { current->bu=o->next; if(current->bu) current->bu->prev=NULL; } // Need to reset if the one that was removed was // selected in ncurses. if(o==*sselbu) *sselbu=NULL; bu_free(&o); o=lasto; } lasto=o; if(o) o=o->next; } // Now, new list only has entries missing from old list. n=bu_list; lastn=NULL; while(n) { o=current->bu; lasto=NULL; while(o && n->bno < o->bno) { lasto=o; o=o->next; } // Found the place to insert it. if(lasto) { lasto->next=n; n->prev=lasto; } else { if(current->bu) current->bu->prev=n; current->bu=n; current->bu->prev=NULL; } lastn=n->next; n->next=o; n=lastn; } }
static int free_prepend_s(char **dst, const char *a, const char *b) { free_w(dst); return !(*dst=prepend_s(a, b)); }
static int do_set_xattr_bsd(struct asfd *asfd, const char *path, struct stat *statp, const char *xattrtext, size_t xlen, struct conf *conf) { int ret=-1; size_t l=0; char *data=NULL; char *value=NULL; char *nspace=NULL; data=(char *)xattrtext; l=xlen; while(l>0) { int cnt; ssize_t vlen=0; int cnspace=0; char *name=NULL; if(!(nspace=get_next_str(asfd, &data, &l, conf, &vlen, path)) || !(value=get_next_str(asfd, &data, &l, conf, &vlen, path))) goto end; // Need to split the name into two parts. if(!(name=strchr(nspace, '.'))) { logw(conf, "could not split %s into namespace and name on %s\n", nspace, path); goto end; } *name='\0'; name++; if(extattr_string_to_namespace(nspace, &cnspace)) { logw(conf, "could not convert %s into namespace on %s", nspace, path); goto end; } //printf("set_link: %d %s %s %s\n", cnspace, nspace, name, value); if((cnt=extattr_set_link(path, cnspace, name, value, vlen))!=vlen) { logw(conf, "extattr_set_link error on %s %d!=vlen: %s\n", path, strerror(errno)); goto end; } free_w(&nspace); free_w(&value); } ret=0; end: free_w(&nspace); free_w(&value); 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 get_xattr(struct asfd *asfd, const char *path, struct stat *statp, char **xattrtext, size_t *xlen, struct conf *conf) { char *z=NULL; size_t len=0; int have_acl=0; char *toappend=NULL; char *xattrlist=NULL; size_t totallen=0; size_t maxlen=0xFFFFFFFF/2; if((len=llistxattr(path, NULL, 0))<=0) { logw(asfd, conf, "could not llistxattr '%s': %d\n", path, len); return 0; // carry on } if(!(xattrlist=(char *)malloc(len+1))) { log_out_of_memory(__func__); return -1; } memset(xattrlist, 0, len+1); if((len=llistxattr(path, xattrlist, len))<=0) { logw(asfd, conf, "could not llistxattr '%s': %d\n", path, len); free_w(&xattrlist); return 0; // carry on } xattrlist[len]='\0'; if(xattrtext && *xattrtext) { // Already have some meta text, which means that some // ACLs were set. have_acl++; } z=xattrlist; for(z=xattrlist; len > (size_t)(z-xattrlist)+1; z=strchr(z, '\0')+1) { char tmp1[9]; char tmp2[9]; char *val=NULL; size_t vlen=0; size_t zlen=0; if((zlen=strlen(z))>maxlen) { logw(asfd, conf, "xattr element of '%s' too long: %d\n", path, zlen); free_w(&toappend); break; } if(have_acl) { int c=0; int skip=0; // skip xattr entries that were already saved as ACLs. for(c=0; xattr_acl_skiplist[c]; c++) { if(!strcmp(z, xattr_acl_skiplist[c])) { skip++; break; } } if(skip) continue; } if((vlen=lgetxattr(path, z, NULL, 0))<=0) { logw(asfd, conf, "could not lgetxattr on %s for %s: %d\n", path, z, vlen); continue; } if(!(val=(char *)malloc(vlen+1))) { log_out_of_memory(__func__); free_w(&xattrlist); free_w(&toappend); return -1; } if((vlen=lgetxattr(path, z, val, vlen))<=0) { logw(asfd, conf, "could not lgetxattr %s for %s: %d\n", path, z, vlen); free_w(&val); continue; } val[vlen]='\0'; if(vlen>maxlen) { logw(asfd, conf, "xattr value of '%s' too long: %d\n", path, vlen); free_w(&toappend); free_w(&val); break; } snprintf(tmp1, sizeof(tmp1), "%08X", (unsigned int)zlen); snprintf(tmp2, sizeof(tmp2), "%08X", (unsigned int)vlen); if(!(toappend=prepend_len(toappend, totallen, tmp1, 8, "", 0, &totallen)) || !(toappend=prepend_len(toappend, totallen, z, zlen, "", 0, &totallen)) || !(toappend=prepend_len(toappend, totallen, tmp2, 8, "", 0, &totallen)) || !(toappend=prepend_len(toappend, totallen, val, vlen, "", 0, &totallen))) { log_out_of_memory(__func__); free_w(&val); free_w(&xattrlist); return -1; } free_w(&val); if(totallen>maxlen) { logw(asfd, conf, "xattr length of '%s' grew too long: %d\n", path, totallen); free_w(&val); free_w(&toappend); free_w(&xattrlist); return 0; // carry on } } if(toappend) { char tmp3[10]; snprintf(tmp3, sizeof(tmp3), "%c%08X", META_XATTR, (unsigned int)totallen); if(!(*xattrtext=prepend_len(*xattrtext, *xlen, tmp3, 9, "", 0, xlen)) || !(*xattrtext=prepend_len(*xattrtext, *xlen, toappend, totallen, "", 0, xlen))) { log_out_of_memory(__func__); free_w(&toappend); free_w(&xattrlist); return -1; } free_w(&toappend); } free_w(&xattrlist); return 0; }
VOID CacheBlockFree(PBLKCACHE pc, LPVOID lpv) { if (IsCacheBlock(pc, lpv)) { PBLKHEADER pbh = (PBLKHEADER)lpv - 1; #ifdef DEBUG PBLKHEADER pbhf = pc->pCacheHead; PBLKHEADER pbhLast = NULL; // remove from the list of working nodes while (NULL != pbhf && pbhf != pbh) { pbhLast = pbhf; pbhf = pbhf->pNext; } if (NULL != pbhf) { // link in pbh->pNext into a worklist LINK_WORKLIST(pc, pbh->pNext, pbhLast); } else { LOGDEBUG(LOG_ALWAYS, ("Alert! CacheBlockFree - invalid ptr\n")); return; } pbhf = pc->pCacheFree; pbhLast = NULL; #else PBLKHEADER pbhf = pc->pCacheFree; PBLKHEADER pbhLast = NULL; #endif // list of free nodes // insert in order while (NULL != pbhf) { // most often case - append from the right if (((LPBYTE)pbhf + pbhf->dwSize) == (LPBYTE)pbh) { pbhf->dwSize += pbh->dwSize; // adjust the size // now see if we need compact if (NULL != pbhLast) { if (((LPBYTE)pbhf + pbhf->dwSize) == (LPBYTE)pbhLast) { // consolidate pbhLast->dwSize += pbhf->dwSize; pbhLast->pNext = pbhf->pNext; } } return; } else // check if we can append from the left if (((LPBYTE)pbh + pbh->dwSize) == (LPBYTE)pbhf) { pbh->dwSize += pbhf->dwSize; // adjust the size pbh->pNext = pbhf->pNext; // next ptr too // now also check the next free ptr so we can compact // the next ptr has lesser address if (NULL != pbh->pNext) { pbhf = pbh->pNext; if (((LPBYTE)pbhf + pbhf->dwSize) == (LPBYTE)pbh) { pbhf->dwSize += pbh->dwSize; pbh = pbhf; } } LINK_FREELIST(pc, pbh, pbhLast); return; } // check for address if (pbh > pbhf) { // we have to link-in a standalone block break; } pbhLast = pbhf; pbhf = pbhf->pNext; // on to the next block } // LOGDEBUG(LOG_ALWAYS, ("Param Map Cache: OUT-OF-ORDER free!!!\n")); pbh->pNext = pbhf; LINK_FREELIST(pc, pbh, pbhLast); } else { free_w(lpv); } }
// returns 1 for finished ok. static int do_stuff_to_receive(struct asfd *asfd, struct sdirs *sdirs, struct conf **cconfs, struct sbuf *rb, struct fzp *chfp, struct dpth *dpth, char **last_requested) { struct iobuf *rbuf=asfd->rbuf; iobuf_free_content(rbuf); // This also attempts to write anything in the write buffer. if(asfd->as->read_write(asfd->as)) { logp("error in %s\n", __func__); return -1; } if(!rbuf->buf) return 0; if(rbuf->cmd==CMD_MESSAGE || rbuf->cmd==CMD_WARNING) { log_recvd(rbuf, cconfs, 0); return 0; } if(rb->protocol1->fzp) { // Currently writing a file (or meta data) switch(rbuf->cmd) { case CMD_APPEND: if(deal_with_receive_append(asfd, rb, cconfs)) goto error; return 0; case CMD_END_FILE: if(deal_with_receive_end_file(asfd, sdirs, rb, chfp, cconfs, last_requested)) goto error; return 0; default: iobuf_log_unexpected(rbuf, __func__); goto error; } } // Otherwise, expecting to be told of a file to save. switch(rbuf->cmd) { case CMD_DATAPTH: iobuf_move(&rb->protocol1->datapth, rbuf); return 0; case CMD_ATTRIBS: iobuf_move(&rb->attr, rbuf); return 0; case CMD_GEN: if(!strcmp(rbuf->buf, "okbackupphase2end")) goto end_phase2; iobuf_log_unexpected(rbuf, __func__); goto error; case CMD_INTERRUPT: // Interrupt - forget about the last requested // file if it matches. Otherwise, we can get // stuck on the select in the async stuff, // waiting for something that will never arrive. if(*last_requested && !strcmp(rbuf->buf, *last_requested)) free_w(last_requested); return 0; default: break; } if(cmd_is_filedata(rbuf->cmd)) { if(deal_with_filedata(asfd, sdirs, rb, rbuf, dpth, cconfs)) goto error; return 0; } iobuf_log_unexpected(rbuf, __func__); error: return -1; end_phase2: return 1; }
static int found_directory(struct asfd *asfd, FF_PKT *ff_pkt, struct conf **confs, char *fname, dev_t parent_device, bool top_level) { int ret=-1; char *link=NULL; size_t link_len; size_t len; int nbret=0; int count=0; dev_t our_device; struct dirent **nl=NULL; our_device=ff_pkt->statp.st_dev; if((nbret=nobackup_directory(get_strlist(confs[OPT_NOBACKUP]), ff_pkt->fname))) { if(nbret<0) goto end; // Error. ret=0; // Do not back it up. goto end; } /* Build a canonical directory name with a trailing slash in link var */ len=strlen(fname); link_len=len+200; if(!(link=(char *)malloc_w(link_len+2, __func__))) goto end; snprintf(link, link_len, "%s", fname); /* Strip all trailing slashes */ while(len >= 1 && IsPathSeparator(link[len - 1])) len--; /* add back one */ link[len++]='/'; link[len]=0; ff_pkt->link=link; ff_pkt->type=FT_DIR; #if defined(HAVE_WIN32) windows_reparse_point_fiddling(ff_pkt); #endif if(send_file_w(asfd, ff_pkt, top_level, confs)) goto end; if(ff_pkt->type==FT_REPARSE || ff_pkt->type==FT_JUNCTION) { // Ignore. ret=0; goto end; } if(top_level || (parent_device!=ff_pkt->statp.st_dev #if defined(HAVE_WIN32) || ff_pkt->statp.st_rdev==WIN32_MOUNT_POINT #endif )) { if(fstype_excluded(asfd, confs, ff_pkt->fname)) { ret=send_file_w(asfd, ff_pkt, top_level, confs); goto end; } if(!top_level && !fs_change_is_allowed(confs, ff_pkt->fname)) { ff_pkt->type=FT_NOFSCHG; // Just backup the directory and return. ret=send_file_w(asfd, ff_pkt, top_level, confs); goto end; } } ff_pkt->link=ff_pkt->fname; errno=0; switch(entries_in_directory_alphasort(fname, &nl, &count, get_int(confs[OPT_ATIME]))) { case 0: break; case 1: ff_pkt->type=FT_NOOPEN; ret=send_file_w(asfd, ff_pkt, top_level, confs); default: goto end; } if(nl) { if(process_entries_in_directory(asfd, nl, count, &link, len, &link_len, confs, ff_pkt, our_device)) goto end; } ret=0; end: free_w(&link); free_v((void **)&nl); return ret; }
int backup_phase2_server_protocol1(struct async *as, struct sdirs *sdirs, const char *incexc, int resume, struct conf **cconfs) { int ret=0; struct manio *p1manio=NULL; struct dpth *dpth=NULL; char *deltmppath=NULL; char *last_requested=NULL; // Where to write changed data. // Data is not getting written to a compressed file. // This is important for recovery if the power goes. struct fzp *chfp=NULL; struct fzp *ucfp=NULL; // unchanged data struct fzp *cmanfp=NULL; // previous (current) manifest. struct sbuf *cb=NULL; // file list in current manifest struct sbuf *p1b=NULL; // file list from client struct sbuf *rb=NULL; // receiving file from client struct asfd *asfd=as->asfd; int breaking=0; int breakcount=0; if(get_int(cconfs[OPT_BREAKPOINT])>=2000 && get_int(cconfs[OPT_BREAKPOINT])<3000) { breaking=get_int(cconfs[OPT_BREAKPOINT]); breakcount=breaking-2000; } logp("Begin phase2 (receive file data)\n"); if(!(dpth=dpth_alloc()) || dpth_protocol1_init(dpth, sdirs->currentdata, get_int(cconfs[OPT_MAX_STORAGE_SUBDIRS]))) goto error; if(open_previous_manifest(&cmanfp, sdirs, incexc, cconfs)) goto error; if(get_int(cconfs[OPT_DIRECTORY_TREE])) { // Need to make sure we do not try to create a path that is // too long. if(build_path_w(sdirs->treepath)) goto error; treepathlen=strlen(sdirs->treepath); init_fs_max(sdirs->treepath); } if(!(p1manio=manio_alloc()) || manio_init_read(p1manio, sdirs->phase1data) || !(cb=sbuf_alloc(cconfs)) || !(p1b=sbuf_alloc(cconfs)) || !(rb=sbuf_alloc(cconfs))) goto error; manio_set_protocol(p1manio, PROTO_1); if(resume && do_resume(p1manio, sdirs, dpth, cconfs)) goto error; // Unchanged and changed should now be truncated correctly, we just // have to open them for appending. if(!(ucfp=fzp_open(sdirs->unchanged, "a+b")) || !(chfp=fzp_open(sdirs->changed, "a+b"))) goto error; if(manio_closed(p1manio) && manio_open_next_fpath(p1manio)) goto error; while(1) { if(breaking) { if(breakcount--==0) return breakpoint(cconfs, __func__); } //printf("in loop, %s %s %c\n", // cmanfp?"got cmanfp":"no cmanfp", // rb->path.buf?:"no rb->path", // rb->path.buf?'X':rb->path.cmd); if(write_status(CNTR_STATUS_BACKUP, rb->path.buf?rb->path.buf:p1b->path.buf, cconfs)) goto error; if(last_requested || manio_closed(p1manio) || asfd->writebuflen) { switch(do_stuff_to_receive(asfd, sdirs, cconfs, rb, chfp, dpth, &last_requested)) { case 0: break; case 1: goto end; // Finished ok. case -1: goto error; } } switch(do_stuff_to_send(asfd, p1b, &last_requested)) { case 0: break; case 1: continue; case -1: goto error; } if(manio_closed(p1manio)) continue; sbuf_free_content(p1b); switch(manio_sbuf_fill_phase1(p1manio, asfd, p1b, NULL, sdirs, cconfs)) { case 0: break; case 1: manio_close(p1manio); if(asfd->write_str(asfd, CMD_GEN, "backupphase2end")) goto error; break; case -1: goto error; } if(!cmanfp) { // No old manifest, need to ask for a new file. if(process_new(sdirs, cconfs, p1b, ucfp)) goto error; continue; } // Have an old manifest, look for it there. // Might already have it, or be ahead in the old // manifest. if(cb->path.buf) switch(maybe_process_file(asfd, sdirs, cb, p1b, ucfp, cconfs)) { case 0: break; case 1: continue; case -1: goto error; } while(cmanfp) { sbuf_free_content(cb); switch(sbufl_fill(cb, asfd, cmanfp, cconfs)) { case 0: break; case 1: fzp_close(&cmanfp); if(process_new(sdirs, cconfs, p1b, ucfp)) goto error; continue; case -1: goto error; } switch(maybe_process_file(asfd, sdirs, cb, p1b, ucfp, cconfs)) { case 0: continue; case 1: break; case -1: goto error; } break; } } error: ret=-1; end: if(fzp_close(&chfp)) { logp("error closing %s in %s\n", sdirs->changed, __func__); ret=-1; } if(fzp_close(&ucfp)) { logp("error closing %s in %s\n", sdirs->unchanged, __func__); ret=-1; } free_w(&deltmppath); sbuf_free(&cb); sbuf_free(&p1b); sbuf_free(&rb); manio_free(&p1manio); fzp_close(&cmanfp); dpth_free(&dpth); if(!ret) unlink(sdirs->phase1data); logp("End phase2 (receive file data)\n"); return ret; }
static int restore_special(struct asfd *asfd, struct sbuf *sb, const char *fname, enum action act, struct cntr *cntr, enum protocol protocol) { int ret=0; char *rpath=NULL; #ifdef HAVE_WIN32 logw(asfd, cntr, "Cannot restore special files to Windows: %s\n", fname); goto end; #else struct stat statp=sb->statp; if(act==ACTION_VERIFY) { cntr_add(cntr, CMD_SPECIAL, 1); return 0; } if(build_path(fname, "", &rpath, NULL)) { // failed - do a warning if(restore_interrupt(asfd, sb, build_msg("build path failed: %s", fname), cntr, protocol)) ret=-1; goto end; } if(S_ISFIFO(statp.st_mode)) { if(mkfifo(rpath, statp.st_mode) && errno!=EEXIST) do_logw(asfd, cntr, "Cannot make fifo: %s\n", strerror(errno)); else { attribs_set(asfd, rpath, &statp, sb->winattr, cntr); cntr_add(cntr, CMD_SPECIAL, 1); } } else if(S_ISSOCK(statp.st_mode)) { if(mksock(rpath)) do_logw(asfd, cntr, "Cannot make socket: %s\n", strerror(errno)); else { attribs_set(asfd, rpath, &statp, sb->winattr, cntr); cntr_add(cntr, CMD_SPECIAL, 1); } } #ifdef S_IFDOOR // Solaris high speed RPC mechanism else if (S_ISDOOR(statp.st_mode)) do_logw(asfd, cntr, "Skipping restore of door file: %s\n", fname); #endif #ifdef S_IFPORT // Solaris event port for handling AIO else if (S_ISPORT(statp.st_mode)) do_logw(asfd, cntr, "Skipping restore of event port file: %s\n", fname); #endif else if(mknod(fname, statp.st_mode, statp.st_rdev) && errno!=EEXIST) do_logw(asfd, cntr, "Cannot make node: %s\n", strerror(errno)); else { attribs_set(asfd, rpath, &statp, sb->winattr, cntr); cntr_add(cntr, CMD_SPECIAL, 1); } #endif end: free_w(&rpath); 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 **cconfs) { int ret=-1; struct stat statp; char *oldpath=NULL; char *newpath=NULL; char *finpath=NULL; char *deltafpath=NULL; const char *datapth=sb->protocol1->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, cconfs)) { logp("error when inflating old file: %s\n", oldpath); free(infpath); goto end; } if((lrs=do_patch(NULL, infpath, deltafpath, newpath, get_int(cconfs[OPT_COMPRESSION]), sb->compression /* from the manifest */, cconfs))) { logp("WARNING: librsync error when patching %s: %d\n", oldpath, lrs); cntr_add(get_cntr(cconfs[OPT_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, cconfs)) 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, cconfs, 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; }
void candidate_free_content(struct candidate *c) { if(!c) return; free_w(&c->path); }
/* 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 **cconfs, unsigned long bno) { 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 error; 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 error; 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(cconfs))) { log_out_of_memory(__func__); goto error; } mkdir(fdirs->datadir, 0777); while(1) { switch(sbufl_fill(sb, NULL, NULL, zp, get_cntr(cconfs[OPT_CNTR]))) { case 0: break; case 1: goto end; default: goto error; } if(sb->protocol1->datapth.buf) { if(write_status(CNTR_STATUS_SHUFFLING, sb->protocol1->datapth.buf, cconfs) || jiggle(sdirs, fdirs, sb, hardlinked_current, deltabdir, deltafdir, sigpath, &delfp, cconfs)) goto error; } sbuf_free_content(sb); } end: if(close_fp(&delfp)) { logp("error closing %s in atomic_data_jiggle\n", fdirs->deletionsfile); goto error; } if(maybe_delete_files_from_manifest(tmpman, fdirs, cconfs)) goto error; // 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 */); ret=0; error: 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; }
static void file_free_content(struct file *file) { if(!file) return; free_w(&file->path); }
void sdirs_free_content(struct sdirs *sdirs) { free_w(&sdirs->base); free_w(&sdirs->dedup); free_w(&sdirs->champlock); free_w(&sdirs->champsock); free_w(&sdirs->champlog); free_w(&sdirs->data); free_w(&sdirs->clients); free_w(&sdirs->client); free_w(&sdirs->working); free_w(&sdirs->rworking); free_w(&sdirs->finishing); free_w(&sdirs->current); free_w(&sdirs->currenttmp); free_w(&sdirs->deleteme); free_w(&sdirs->timestamp); free_w(&sdirs->changed); free_w(&sdirs->unchanged); free_w(&sdirs->manifest); free_w(&sdirs->rmanifest); free_w(&sdirs->cmanifest); free_w(&sdirs->phase1data); free_w(&sdirs->lockdir); lock_free(&sdirs->lock); // Burp1 directories. free_w(&sdirs->currentdata); free_w(&sdirs->datadirtmp); free_w(&sdirs->phase2data); free_w(&sdirs->unchangeddata); free_w(&sdirs->cincexc); free_w(&sdirs->deltmppath); free_w(&sdirs->treepath); }
static int check_files(struct mystruct *find, struct file *newfile, struct stat *info, const char *ext, unsigned int maxlinks) { int found=0; struct fzp *nfp=NULL; struct fzp *ofp=NULL; struct file *f=NULL; for(f=find->files; f; f=f->next) { //printf(" against: '%s'\n", f->path); if(!f->path) { // If the full_match() function fails to open oldfile // (which could happen if burp deleted some old // directories), it will free path and set it to NULL. // Skip entries like this. continue; } if(newfile->dev!=f->dev) { // Different device. continue; } if(newfile->ino==f->ino) { // Same device, same inode, therefore these two files // are hardlinked to each other already. found++; break; } if((!newfile->part_cksum && get_part_cksum(newfile, &nfp)) || (!f->part_cksum && get_part_cksum(f, &ofp))) { // Some error with md5sums Give up. return -1; } if(newfile->part_cksum!=f->part_cksum) { fzp_close(&ofp); continue; } //printf(" %s, %s\n", find->files->path, newfile->path); //printf(" part cksum matched\n"); if((!newfile->full_cksum && get_full_cksum(newfile, &nfp)) || (!f->full_cksum && get_full_cksum(f, &ofp))) { // Some error with md5sums Give up. return -1; } if(newfile->full_cksum!=f->full_cksum) { fzp_close(&ofp); continue; } //printf(" full cksum matched\n"); if(!full_match(newfile, f, &nfp, &ofp)) { fzp_close(&ofp); continue; } //printf(" full match\n"); //printf("%s, %s\n", find->files->path, newfile->path); // If there are already enough links to this file, replace // our memory of it with the new file so that files later on // can link to the new one. if(f->nlink>=maxlinks) { // Just need to reset the path name and the number // of links, and pretend that it was found otherwise // NULL newfile will get added to the memory. reset_old_file(f, newfile, info); found++; break; } found++; count++; if(verbose) printf("%s\n", newfile->path); // Now hardlink it. if(makelinks) { switch(do_hardlink(newfile, f, ext)) { case 0: f->nlink++; // Only count bytes as saved if we // removed the last link. if(newfile->nlink==1) savedbytes+=info->st_size; break; case -1: // On error, replace the memory of the // old file with the one that we just // found. It might work better when // someone later tries to link to the // new one instead of the old one. reset_old_file(f, newfile, info); count--; break; default: // Abandon all hope. // This could happen if renaming the // hardlink failed in such a way that // the target file was unlinked without // being replaced - ie, if the max // number of hardlinks is being hit. return -1; } } else if(deletedups) { if(unlink(newfile->path)) { logp("Could not delete %s: %s\n", newfile->path, strerror(errno)); } else { // Only count bytes as saved if we removed the // last link. if(newfile->nlink==1) savedbytes+=info->st_size; } } else { // To be able to tell how many bytes // are saveable. savedbytes+=info->st_size; } break; } fzp_close(&nfp); fzp_close(&ofp); if(found) { free_w(&newfile->path); return 0; } if(add_file(find, newfile)) return -1; return 0; }
static int jiggle(struct sdirs *sdirs, struct fdirs *fdirs, struct sbuf *sb, int hardlinked_current, const char *deltabdir, const char *deltafdir, const char *sigpath, struct fzp **delfp, struct conf **cconfs) { int ret=-1; struct stat statp; char *oldpath=NULL; char *newpath=NULL; char *finpath=NULL; char *deltafpath=NULL; const char *datapth=sb->protocol1->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(!unlink(deltafpath)) logp("deleted unneeded forward delta: %s\n", 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++; } ret=0; goto end; } if(mkpath(&finpath, fdirs->datadir)) { logp("could not create path for: %s\n", finpath); goto end; } if(!lstat(deltafpath, &statp) && S_ISREG(statp.st_mode)) { if(mkpath(&newpath, fdirs->datadirtmp)) { logp("could not create path for: %s\n", newpath); goto end; } ret=forward_patch_and_reverse_diff( fdirs, delfp, deltabdir, deltafdir, deltafpath, sigpath, oldpath, newpath, datapth, finpath, hardlinked_current, sb, cconfs ); goto end; } 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"); ret=do_rename(newpath, finpath); goto end; } 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, cconfs, 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); } } ret=0; goto end; } logp("could not find: %s\n", oldpath); end: free_w(&oldpath); free_w(&newpath); free_w(&finpath); free_w(&deltafpath); return ret; }