static int maybe_send_extrameta(const char *path, char cmd, const char *attribs, struct cntr *p1cntr) { if(has_extrameta(path, cmd)) { if(async_write_str(CMD_STAT, attribs) || async_write_str(metasymbol, path)) return -1; do_filecounter(p1cntr, metasymbol, 1); } return 0; }
static int do_start(const char *reqstr, const char *repstr) { int ret=-1; char cmd='\0'; char *buf=NULL; size_t len=0; if(async_write_str(CMD_GEN, reqstr)) goto end; if(async_read(&cmd, &buf, &len)) goto end; if(cmd==CMD_GEN) { if(strcmp(buf, repstr)) { logp("unexpected response to %s: %s\n", reqstr, buf); goto end; } } else { logp("unexpected response to %s: %c:%s\n", reqstr, cmd, buf); goto end; } ret=0; end: if(buf) free(buf); return ret; }
void log_and_send_oom(const char *function) { char m[256]=""; snprintf(m, sizeof(m), "out of memory in %s()\n", __FUNCTION__); logp("%s", m); if(fd>0) async_write_str(CMD_ERROR, m); }
int do_delete_client(struct config *conf) { char msg[128]=""; snprintf(msg, sizeof(msg), "delete %s", conf->backup?conf->backup:""); if(async_write_str(CMD_GEN, msg) || async_read_expect(CMD_GEN, "ok")) return -1; logp("Deletion in progress\n"); return 0; }
static int send_incexc_str(const char *pre, const char *str) { char *tosend=NULL; if(!str) return 0; if(!(tosend=prepend(pre, str, strlen(str), " = "))) return -1; if(async_write_str(CMD_GEN, tosend)) { logp("Error in async_write_str when sending incexc\n"); return -1; } return 0; }
static int do_write(BFILE *bfd, FILE *fp, unsigned char *out, size_t outlen, char **metadata, unsigned long long *sent) { int ret=0; if(metadata) { // Append it to our metadata. out[outlen]='\0'; //printf("\nadd outlen: %lu\n", outlen); if(!(*metadata=prepend_len(*metadata, *sent, (const char *)out, outlen, "", 0, (size_t *)sent))) { logp("error when appending metadata\n"); async_write_str(CMD_ERROR, "error when appending metadata"); return -1; } } else { #ifdef HAVE_WIN32 if((ret=bwrite(bfd, out, outlen))<=0) { logp("error when appending: %d\n", ret); async_write_str(CMD_ERROR, "write failed"); return -1; } #else if((fp && (ret=fwrite(out, 1, outlen, fp))<=0)) { logp("error when appending: %d\n", ret); async_write_str(CMD_ERROR, "write failed"); return -1; } #endif *sent+=outlen; } return 0; }
int send_counters(const char *client, struct cntr *p1cntr, struct cntr *cntr) { char buf[4096]=""; counters_to_str(buf, sizeof(buf), client, STATUS_RUNNING, " " /* normally the path for status server */, p1cntr, cntr); if(async_write_str(CMD_GEN, buf)) { logp("Error when sending counters to client.\n"); return -1; } return 0; }
int incexc_recv_server(char **incexc, struct config *conf, struct cntr *p1cntr) { int ret=-1; char *tmp=NULL; char *buf=NULL; if(async_write_str(CMD_GEN, "incexc ok")) goto end; while(1) { char cmd; size_t len=0; if(async_read(&cmd, &buf, &len)) break; if(cmd==CMD_GEN) { if(!strcmp(buf, "incexc end")) { if(async_write_str(CMD_GEN, "incexc end ok")) goto end; ret=0; break; } if(!(tmp=prepend(*incexc?:"", buf, strlen(buf), *incexc?"\n":""))) goto end; if(*incexc) free(*incexc); *incexc=tmp; } else { logp("unexpected command from client when receiving incexc: %c:%s\n", cmd, buf); break; } if(buf) { free(buf); buf=NULL; } }
int write_endfile(unsigned long long bytes, unsigned char *checksum) { int ret=0; char endmsg[128]=""; snprintf(endmsg, sizeof(endmsg), #ifdef HAVE_WIN32 "%I64u:%s", #else "%llu:%s", #endif bytes, get_checksum_str(checksum)); ret=async_write_str(CMD_END_FILE, endmsg); return ret; }
// should be in src/lib/log.c int logw(struct cntr *cntr, const char *fmt, ...) { int r=0; char buf[512]=""; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); if(doing_estimate) printf("\nWARNING: %s\n", buf); else { r=async_write_str(CMD_WARNING, buf); logp("WARNING: %s\n", buf); } va_end(ap); do_filecounter(cntr, CMD_WARNING, 1); return r; }
// Return 0 for OK, -1 for error, 1 for timer conditions not met. static int maybe_check_timer(const char *phase1str, struct config *conf, int *resume) { char rcmd=0; char *rdst=NULL; size_t rlen=0; int complen=0; if(async_write_str(CMD_GEN, phase1str)) return -1; if(async_read(&rcmd, &rdst, &rlen)) return -1; if(rcmd==CMD_GEN && !strcmp(rdst, "timer conditions not met")) { free(rdst); logp("Timer conditions on the server were not met\n"); return 1; } else if(rcmd!=CMD_GEN) { logp("unexpected command from server: %c:%s\n", rcmd ,rdst); free(rdst); return -1; } if(!strncmp(rdst, "ok", 2)) complen=3; else if(!strncmp(rdst, "resume", 6)) { complen=7; *resume=1; logp("server wants to resume previous backup.\n"); } else { logp("unexpected command from server: %c:%s\n", rcmd ,rdst); free(rdst); return -1; } // The server now tells us the compression level in the OK response. if(strlen(rdst)>3) conf->compression=atoi(rdst+complen); logp("Compression level: %d\n", conf->compression); return 0; }
static int forget_file(struct sbuf *sb, char cmd, struct cntr *cntr) { // Tell the server to forget about this // file, otherwise it might get stuck // on a select waiting for it to arrive. if(async_write_str(CMD_INTERRUPT, sb->path)) return 0; if(cmd==CMD_FILE && sb->datapth) { rs_signature_t *sumset=NULL; // The server will be sending // us a signature. Munch it up // then carry on. if(load_signature(&sumset, cntr)) return -1; else rs_free_sumset(sumset); } return 0; }
static int do_restore_end(enum action act, struct cntr *cntr) { char cmd; int ret=0; int quit=0; size_t len=0; if(async_write_str(CMD_GEN, "restoreend")) ret=-1; while(!ret && !quit) { char *buf=NULL; if(async_read(&cmd, &buf, &len)) { ret=-1; quit++; } else if(cmd==CMD_GEN && !strcmp(buf, "restoreend ok")) { logp("got restoreend ok\n"); quit++; } else if(cmd==CMD_WARNING) { logp("WARNING: %s\n", buf); do_filecounter(cntr, cmd, 0); } else if(cmd==CMD_INTERRUPT) { // ignore - client wanted to interrupt a file } else { logp("unexpected cmd from client at end of restore: %c:%s\n", cmd, buf); ret=-1; quit++; } if(buf) { free(buf); buf=NULL; } } return ret; }
void log_and_send(const char *msg) { logp("%s\n", msg); if(fd>0) async_write_str(CMD_ERROR, msg); }
/* Return 1 for everything OK, signed and returned, -1 for error, 0 for nothing done. */ int ca_server_maybe_sign_client_cert(const char *client, const char *cversion, struct config *conf, struct cntr *p1cntr) { int ret=0; char *buf=NULL; long min_ver=0; long cli_ver=0; if((min_ver=version_to_long("1.3.2"))<0 || (cli_ver=version_to_long(cversion))<0) return -1; // Clients before 1.3.2 did not know how to send cert signing requests. if(cli_ver<min_ver) return 0; while(1) { char cmd; size_t len=0; if(async_read(&cmd, &buf, &len)) { ret=-1; break; } if(cmd==CMD_GEN) { if(!strcmp(buf, "csr")) { // Client wants to sign a certificate. logp("Client %s wants a certificate signed\n", client); if(!conf->ca_conf || !gca_dir) { logp("But server is not configured to sign client certificate requests.\n"); logp("See option 'ca_conf'.\n"); async_write_str(CMD_ERROR, "server not configured to sign client certificates"); ret=-1; break; } // sign_client_cert() will return 1 for // everything signed and returned, or -1 // for error ret=sign_client_cert(client, conf, p1cntr); break; } else if(!strcmp(buf, "nocsr")) { // Client does not want to sign a certificate. // No problem, just carry on. logp("Client %s does not want a certificate signed\n", client); ret=async_write_str(CMD_GEN, "nocsr ok"); break; } else { logp("unexpected command from client when expecting csr: %c:%s\n", cmd, buf); ret=-1; break; } } else { logp("unexpected command from client when expecting csr: %c:%s\n", cmd, buf); ret=-1; break; } if(buf) free(buf); buf=NULL; } if(buf) free(buf); return ret; }
/* Return 1 for everything OK, signed and returned, -1 for error */ static int sign_client_cert(const char *client, struct config *conf, struct cntr *p1cntr) { int ret=-1; char msg[256]=""; char csrpath[512]=""; char crtpath[512]=""; struct stat statp; snprintf(csrpath, sizeof(csrpath), "%s/%s.csr", gca_dir, client); snprintf(crtpath, sizeof(crtpath), "%s/%s.crt", gca_dir, client); if(!strcmp(client, conf->ca_name)) { char msg[512]=""; snprintf(msg, sizeof(msg), "Will not accept a client certificate request with the same name as the CA (%s)!", conf->ca_name); log_and_send(msg); // Do not goto end, as it will delete things; return -1; } if(!lstat(crtpath, &statp)) { char msg[512]=""; snprintf(msg, sizeof(msg), "Will not accept a client certificate request for '%s' - %s already exists!", client, crtpath); log_and_send(msg); // Do not goto end, as it will delete things; return -1; } if(!lstat(csrpath, &statp)) { char msg[512]=""; snprintf(msg, sizeof(msg), "Will not accept a client certificate request for '%s' - %s already exists!", client, csrpath); log_and_send(msg); // Do not goto end, as it will delete things; return -1; } // Tell the client that we will do it, and send the server name at the // same time. snprintf(msg, sizeof(msg), "csr ok:%s", conf->ca_server_name); if(async_write_str(CMD_GEN, msg)) { // Do not goto end, as it will delete things; return -1; } /* After this point, we might have uploaded files, so on error, go to end and delete any new files. */ // Get the CSR from the client. if(receive_a_file(csrpath, p1cntr)) goto end; // Now, sign it. logp("Signing certificate signing request from %s\n", client); logp("Running '%s --name %s --ca %s --sign --batch --dir %s --config %s'\n", conf->ca_burp_ca, client, conf->ca_name, gca_dir, conf->ca_conf); if(run_script(conf->ca_burp_ca, NULL, 0, "--name", client, "--ca", conf->ca_name, "--sign", "--batch", "--dir", gca_dir, "--config", conf->ca_conf, NULL /* cntr */, 1 /* wait */, 0 /* do not use logp - stupid openssl prints lots of dots one at a time with no way to turn it off */)) { logp("Error running %s\n", conf->ca_burp_ca); goto end; } // Now, we should have a signed certificate. // Need to send it back to the client. if(send_a_file(crtpath, p1cntr)) goto end; // Also need to send the CA public certificate back to the client. if(send_a_file(conf->ssl_cert_ca, p1cntr)) goto end; ret=1; end: if(ret<0) { unlink(crtpath); unlink(csrpath); } return ret; }
// Return -1 on error or success, 0 to continue normally. int autoupgrade_server(long ser_ver, long cli_ver, const char *os, struct config *conf, struct cntr *p1cntr) { int ret=-1; char *path=NULL; char *base_path=NULL; char *script_path=NULL; char *package_path=NULL; char *script_path_top=NULL; char *script_path_specific=NULL; struct stat stats; struct stat statp; if(!conf->autoupgrade_dir) { // Autoupgrades not turned on on the server. ret=0; async_write_str(CMD_GEN, "do not autoupgrade"); goto end; } if(cli_ver>=ser_ver) { // No need to upgrade - client is same version as server, // or newer. ret=0; async_write_str(CMD_GEN, "do not autoupgrade"); goto end; } if(!(base_path=prepend_s(conf->autoupgrade_dir, os, strlen(os))) || !(path=prepend_s(base_path, VERSION, strlen(VERSION))) || !(script_path_top=prepend_s(base_path, "script", strlen("script"))) || !(script_path_specific=prepend_s(path, "script", strlen("script"))) || !(package_path=prepend_s(path, "package", strlen("package")))) { async_write_str(CMD_GEN, "do not autoupgrade"); goto end; } if(!stat(script_path_specific, &stats)) script_path=script_path_specific; else if(!stat(script_path_top, &stats)) script_path=script_path_top; else { logp("Want to autoupgrade client, but no file at:\n"); logp("%s\n", script_path_top); logp("or:\n"); logp("%s\n", script_path_specific); ret=0; // this is probably OK async_write_str(CMD_GEN, "do not autoupgrade"); goto end; } if(stat(package_path, &statp)) { logp("Want to autoupgrade client, but no file available at:\n"); logp("%s\n", package_path); ret=0; // this is probably OK async_write_str(CMD_GEN, "do not autoupgrade"); goto end; } if(!S_ISREG(stats.st_mode)) { logp("%s is not a regular file\n", script_path); async_write_str(CMD_GEN, "do not autoupgrade"); goto end; } if(!S_ISREG(statp.st_mode)) { logp("%s is not a regular file\n", package_path); async_write_str(CMD_GEN, "do not autoupgrade"); goto end; } if(async_write_str(CMD_GEN, "autoupgrade ok")) goto end; if(send_a_file(script_path, p1cntr)) { logp("Problem sending %s\n", script_path); goto end; } if(send_a_file(package_path, p1cntr)) { logp("Problem sending %s\n", package_path); goto end; } ret=0; /* Clients currently exit after forking, so exit ourselves. */ logp("Expecting client to upgrade - now exiting\n"); async_free(); exit(0); end: if(path) free(path); if(base_path) free(base_path); if(script_path_specific) free(script_path_specific); if(script_path_top) free(script_path_top); if(package_path) free(package_path); return ret; }
/* Return 1 for everything OK, signed and returned, -1 for error, 0 for nothing done. */ int ca_client_setup(struct config *conf, struct cntr *p1cntr) { char cmd; int ret=-1; size_t len=0; char *buf=NULL; char csr_path[256]=""; char ssl_cert_tmp[512]=""; char ssl_cert_ca_tmp[512]=""; struct stat statp; // Do not continue if we have none of the following things set. if( !conf->ca_burp_ca || !conf->ca_csr_dir || !conf->ssl_cert_ca || !conf->ssl_cert || !conf->ssl_key // Do not try to get a new certificate if we already have a // key. || !lstat(conf->ssl_key, &statp)) { if(async_write_str(CMD_GEN, "nocsr") || async_read_expect(CMD_GEN, "nocsr ok")) { logp("problem reading from server nocsr\n"); return -1; } logp("nocsr ok\n"); return 0; } // Tell the server we want to do a signing request. if(async_write_str(CMD_GEN, "csr")) return -1; if(async_rw_ensure_read(&cmd, &buf, &len, '\0', NULL, 0)) { logp("problem reading from server csr\n"); goto end; } if(cmd!=CMD_GEN || strncmp(buf, "csr ok:", strlen("csr ok:"))) { logp("unexpected command from server: %c:%s\n", cmd, buf); goto end; } // The server appends its name after 'csr ok:' if(conf->ssl_peer_cn) free(conf->ssl_peer_cn); if(!(conf->ssl_peer_cn=strdup(buf+strlen("csr ok:")))) { logp("out of memory\n"); goto end; } logp("Server will sign a certificate request\n"); // First need to generate a client key and a certificate signing // request. snprintf(csr_path, sizeof(csr_path), "%s/%s.csr", conf->ca_csr_dir, conf->cname); if(generate_key_and_csr(conf, csr_path)) goto end; // Then copy the csr to the server. if(send_a_file(csr_path, p1cntr)) goto end; snprintf(ssl_cert_tmp, sizeof(ssl_cert_tmp), "%s.%d", conf->ssl_cert, getpid()); snprintf(ssl_cert_ca_tmp, sizeof(ssl_cert_ca_tmp), "%s.%d", conf->ssl_cert_ca, getpid()); // The server will then sign it, and give it back. if(receive_a_file(ssl_cert_tmp, p1cntr)) goto end; // The server will also send the CA certificate. if(receive_a_file(ssl_cert_ca_tmp, p1cntr)) goto end; if(do_rename(ssl_cert_tmp, conf->ssl_cert) || do_rename(ssl_cert_ca_tmp, conf->ssl_cert_ca)) goto end; // Need to rewrite our configuration file to contain the server // name (ssl_peer_cn) if(rewrite_client_conf(conf)) goto end; // My goodness, everything seems to have gone OK. Stand back! ret=1; end: if(buf) free(buf); if(ret<0) { // On error, remove any possibly newly created files, so that // this function might run again on another go. unlink(csr_path); unlink(conf->ssl_key); unlink(conf->ssl_cert); unlink(conf->ssl_cert_ca); unlink(ssl_cert_tmp); unlink(ssl_cert_ca_tmp); } return ret; }
/* May return 1 to mean try again. This happens after a successful certificate signing request so that it connects again straight away with the new key/certificate. Returns 2 if there were restore/verify warnings. Returns 3 if timer conditions were not met. */ static int do_client(struct config *conf, enum action act, int vss_restore, int json) { int ret=0; int rfd=-1; int resume=0; SSL *ssl=NULL; BIO *sbio=NULL; char buf[256]=""; SSL_CTX *ctx=NULL; struct cntr cntr; struct cntr p1cntr; char *incexc=NULL; char *server_version=NULL; const char *phase1str="backupphase1"; reset_filecounter(&p1cntr, time(NULL)); reset_filecounter(&cntr, time(NULL)); setup_signals_client(); // settimers(0, 100); logp("begin client\n"); if(act!=ACTION_ESTIMATE) { ssl_load_globals(); if(!(ctx=ssl_initialise_ctx(conf))) { logp("error initialising ssl ctx\n"); ret=-1; goto end; } SSL_CTX_set_session_id_context(ctx, (const unsigned char *)&s_server_session_id_context, sizeof(s_server_session_id_context)); if((rfd=init_client_socket(conf->server, conf->port))<0) { ret=-1; goto end; } if(!(ssl=SSL_new(ctx)) || !(sbio=BIO_new_socket(rfd, BIO_NOCLOSE))) { ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); logp("Problem joining SSL to the socket: %s\n", buf); ret=-1; goto end; } SSL_set_bio(ssl, sbio, sbio); if(SSL_connect(ssl)<=0) { ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); logp("SSL connect error: %s\n", buf); ret=-1; goto end; } } if((ret=async_init(rfd, ssl, conf, act==ACTION_ESTIMATE))) goto end; // Set quality of service bits on backup packets. if(act==ACTION_BACKUP || act==ACTION_BACKUP_TIMED) set_bulk_packets(); if(act!=ACTION_ESTIMATE) { char cmd=0; size_t len=0; char *feat=NULL; int ca_ret=0; if((ret=authorise_client(conf, &server_version, &p1cntr))) goto end; if(server_version) { logp("Server version: %s\n", server_version); // Servers before 1.3.2 did not tell us their versions. // 1.3.2 and above can do the automatic CA stuff that // follows. if((ca_ret=ca_client_setup(conf, &p1cntr))<0) { // Error logp("Error with certificate signing request\n"); ret=-1; goto end; } else if(ca_ret>0) { // Certificate signed successfully. // Everything is OK, but we will reconnect now, in // order to use the new keys/certificates. ret=1; goto end; } } set_non_blocking(rfd); if((ret=ssl_check_cert(ssl, conf))) { logp("check cert failed\n"); goto end; } if((ret=async_write_str(CMD_GEN, "extra_comms_begin"))) { logp("Problem requesting extra_comms_begin\n"); goto end; } // Servers greater than 1.3.0 will list the extra_comms // features they support. else if((ret=async_read(&cmd, &feat, &len))) { logp("Problem reading response to extra_comms_begin\n"); goto end; } else if(cmd!=CMD_GEN) { logp("Unexpected command from server when reading response to extra_comms_begin: %c:%s\n", cmd, feat); ret=-1; goto end; } else if(strncmp(feat, "extra_comms_begin ok", strlen("extra_comms_begin ok"))) { logp("Unexpected response from server when reading response to extra_comms_begin: %c:%s\n", cmd, feat); ret=-1; goto end; } // Can add extra bits here. The first extra bit is the // autoupgrade stuff. if(server_supports_autoupgrade(feat)) { if(conf->autoupgrade_dir && conf->autoupgrade_os && (ret=autoupgrade_client(conf, &p1cntr))) goto end; } // :srestore: means that the server wants to do a restore. if(server_supports(feat, ":srestore:")) { if(conf->server_can_restore) { logp("Server is initiating a restore\n"); if(incexc) { free(incexc); incexc=NULL; } if((ret=incexc_recv_client_restore(&incexc, conf, &p1cntr))) goto end; if(incexc) { if((ret=parse_incexcs_buf(conf, incexc))) goto end; act=ACTION_RESTORE; log_restore_settings(conf, 1); } } else { logp("Server wants to initiate a restore\n"); logp("Client configuration says no\n"); if(async_write_str(CMD_GEN, "srestore not ok")) { ret=-1; goto end; } } } if(conf->orig_client) { char str[512]=""; snprintf(str, sizeof(str), "orig_client=%s", conf->orig_client); if(!server_supports(feat, ":orig_client:")) { logp("Server does not support switching client.\n"); ret=-1; goto end; } if((ret=async_write_str(CMD_GEN, str)) || (ret=async_read_expect(CMD_GEN, "orig_client ok"))) { logp("Problem requesting %s\n", str); ret=-1; goto end; } logp("Switched to client %s\n", conf->orig_client); } // :sincexc: is for the server giving the client the // incexc config. if(act==ACTION_BACKUP || act==ACTION_BACKUP_TIMED) { if(!incexc && server_supports(feat, ":sincexc:")) { logp("Server is setting includes/excludes.\n"); if(incexc) { free(incexc); incexc=NULL; } if((ret=incexc_recv_client(&incexc, conf, &p1cntr))) goto end; if(incexc && (ret=parse_incexcs_buf(conf, incexc))) goto end; } } if(server_supports(feat, ":counters:")) { if(async_write_str(CMD_GEN, "countersok")) goto end; conf->send_client_counters=1; } // :incexc: is for the client sending the server the // incexc config so that it better knows what to do on // resume. if(server_supports(feat, ":incexc:") && (ret=incexc_send_client(conf, &p1cntr))) goto end; if((ret=async_write_str(CMD_GEN, "extra_comms_end")) || (ret=async_read_expect(CMD_GEN, "extra_comms_end ok"))) { logp("Problem requesting extra_comms_end\n"); goto end; } if(feat) free(feat); } rfd=-1; switch(act) { case ACTION_BACKUP_TIMED: phase1str="backupphase1timed"; case ACTION_BACKUP: { // Set bulk packets quality of service flags on backup. if(incexc) { logp("Server is overriding the configuration\n"); logp("with the following settings:\n"); if(log_incexcs_buf(incexc)) { ret=-1; goto end; } } if(!conf->sdcount) { logp("Found no include paths!\n"); ret=-1; goto end; } if(!(ret=maybe_check_timer(phase1str, conf, &resume))) { if(conf->backup_script_pre) { int a=0; const char *args[12]; args[a++]=conf->backup_script_pre; args[a++]="pre"; args[a++]="reserved2"; args[a++]="reserved3"; args[a++]="reserved4"; args[a++]="reserved5"; args[a++]=NULL; if(run_script(args, conf->backup_script_pre_arg, conf->bprecount, &p1cntr, 1, 1)) ret=-1; } if(!ret && do_backup_client(conf, resume, 0, &p1cntr, &cntr)) ret=-1; if((conf->backup_script_post_run_on_fail || !ret) && conf->backup_script_post) { int a=0; const char *args[12]; args[a++]=conf->backup_script_post; args[a++]="post"; // Tell post script whether the restore // failed. args[a++]=ret?"1":"0"; args[a++]="reserved3"; args[a++]="reserved4"; args[a++]="reserved5"; args[a++]=NULL; if(run_script(args, conf->backup_script_post_arg, conf->bpostcount, &cntr, 1, 1)) ret=-1; } } if(ret<0) logp("error in backup\n"); else if(ret>0) { // Timer script said no. // Have a distinct return value to // differentiate between other cases // (ssl reconnection and restore/verify // warnings). ret=3; } else logp("backup finished ok\n"); break; } case ACTION_RESTORE: case ACTION_VERIFY: { if(conf->restore_script_pre) { int a=0; const char *args[12]; args[a++]=conf->restore_script_pre; args[a++]="pre"; args[a++]="reserved2"; args[a++]="reserved3"; args[a++]="reserved4"; args[a++]="reserved5"; args[a++]=NULL; if(run_script(args, conf->restore_script_pre_arg, conf->rprecount, &cntr, 1, 1)) ret=-1; } if(!ret && do_restore_client(conf, act, vss_restore, &p1cntr, &cntr)) ret=-1; if((conf->restore_script_post_run_on_fail || !ret) && conf->restore_script_post) { int a=0; const char *args[12]; args[a++]=conf->restore_script_pre; args[a++]="post"; // Tell post script whether the restore // failed. args[a++]=ret?"1":"0"; args[a++]="reserved3"; args[a++]="reserved4"; args[a++]="reserved5"; args[a++]=NULL; if(run_script(args, conf->restore_script_post_arg, conf->rpostcount, &cntr, 1, 1)) ret=-1; } // Return non-zero if there were warnings, // so that the test script can easily check. if(p1cntr.warning+cntr.warning) ret=2; break; } case ACTION_ESTIMATE: if(!ret) ret=do_backup_client(conf, 0, 1, &p1cntr, &cntr); break; case ACTION_DELETE: if(!ret) ret=do_delete_client(conf); break; case ACTION_LIST: case ACTION_LONG_LIST: default: ret=do_list_client(conf, act, json); break; } end: close_fd(&rfd); async_free(); if(act!=ACTION_ESTIMATE) ssl_destroy_ctx(ctx); if(incexc) free(incexc); if(server_version) free(server_version); //logp("end client\n"); return ret; }
int do_list_client(struct config *conf, enum action act, int json) { int ret=0; size_t slen=0; char msg[512]=""; char scmd=0; struct stat statp; char *statbuf=NULL; char ls[2048]=""; char *dpth=NULL; int first_entry=1; // format long list as JSON int emit_json = (act==ACTION_LONG_LIST && conf->backup && json); //logp("in do_list\n"); if(conf->browsedir) snprintf(msg, sizeof(msg), "listb %s:%s", conf->backup?conf->backup:"", conf->browsedir); else snprintf(msg, sizeof(msg), "list %s:%s", conf->backup?conf->backup:"", conf->regex?conf->regex:""); if(async_write_str(CMD_GEN, msg) || async_read_expect(CMD_GEN, "ok")) return -1; if(emit_json) { printf("{\n"); } // This should probably should use the sbuf stuff. while(1) { char fcmd=0; size_t flen=0; int64_t winattr=0; int compression=-1; char *fname=NULL; if(async_read(&scmd, &statbuf, &slen)) { //ret=-1; break; break; } if(scmd==CMD_TIMESTAMP) { // A backup timestamp, just print it. if(emit_json) { printf("\t\"backup\":\n" "\t\t{\n" "\t\t\t\"timestamp\": \"%s\",\n" "\t\t\t\"directory\": \"%s\",\n" "\t\t\t\"regex\": \"%s\"\n" "\t\t},\n", statbuf, conf->browsedir? conf->browsedir:"", conf->regex? conf->regex:""); } else { printf("Backup: %s\n", statbuf); if(conf->browsedir) printf("Listing directory: %s\n", conf->browsedir); if(conf->regex) printf("With regex: %s\n", conf->regex); } if(statbuf) { free(statbuf); statbuf=NULL; } continue; } else if(scmd==CMD_DATAPTH) { if(statbuf) { free(statbuf); statbuf=NULL; } continue; } else if(scmd!=CMD_STAT) { logp("expected %c cmd - got %c:%s\n", CMD_STAT, scmd, statbuf); ret=-1; break; } decode_stat(statbuf, &statp, &winattr, &compression); if(async_read(&fcmd, &fname, &flen)) { logp("got stat without an object\n"); ret=-1; break; } else if(fcmd==CMD_DIRECTORY || fcmd==CMD_FILE || fcmd==CMD_ENC_FILE || fcmd==CMD_EFS_FILE || fcmd==CMD_SPECIAL) { *ls='\0'; if(act==ACTION_LONG_LIST) { if(emit_json) { char *esc_fname = NULL; ls_output_json(ls, sizeof(ls), first_entry, fcmd, fname, &esc_fname, NULL, NULL, &statp); printf(ls, esc_fname? esc_fname:"", ""); if(esc_fname) free(esc_fname); } else { ls_output(ls, fname, &statp); printf("%s\n", ls); } } else { printf("%s\n", fname); } if (first_entry) { first_entry = 0; } } else if(cmd_is_link(fcmd)) // symlink or hardlink { char lcmd=0; size_t llen=0; char *lname=NULL; if(async_read(&lcmd, &lname, &llen) || lcmd!=fcmd) { logp("could not get second part of link %c:%s\n", fcmd, fname); ret=-1; } else { if(act==ACTION_LONG_LIST) { *ls='\0'; if(emit_json) { char *esc_fname = NULL; char *esc_lname = NULL; ls_output_json(ls, sizeof(ls), first_entry, fcmd, fname, &esc_fname, lname, &esc_lname, &statp); printf(ls, esc_fname? esc_fname:"", esc_lname? esc_lname:""); if(esc_fname) free(esc_fname); if(esc_lname) free(esc_lname); } else { ls_output(ls, fname, &statp); printf("%s -> %s\n", ls, lname); } } else { printf("%s\n", fname); } if (first_entry) { first_entry = 0; } } if(lname) free(lname); } else { fprintf(stderr, "unlistable %c:%s\n", fcmd, fname?:""); } if(fname) free(fname); if(statbuf) { free(statbuf); statbuf=NULL; } } if(statbuf) free(statbuf); if(dpth) free(dpth); if(emit_json) { if(!first_entry) { printf("\t\t]\n"); } printf("}\n"); } if(!ret) logp("List finished ok\n"); return ret; }
static int do_backup_phase2_client(struct config *conf, int resume, struct cntr *cntr) { int ret=0; int quit=0; char cmd; char *buf=NULL; size_t len=0; char attribs[MAXSTRING]; struct sbuf sb; init_sbuf(&sb); if(!resume) { // Only do this bit if the server did not tell us to resume. if(async_write_str(CMD_GEN, "backupphase2") || async_read_expect(CMD_GEN, "ok")) return -1; } while(!quit) { if(async_read(&cmd, &buf, &len)) { ret=-1; quit++; } else if(buf) { //logp("now: %c:%s\n", cmd, buf); if(cmd==CMD_DATAPTH) { sb.datapth=buf; buf=NULL; continue; } else if(cmd==CMD_STAT) { // Ignore the stat data - we will fill it // in again. Some time may have passed by now, // and it is best to make it as fresh as // possible. free(buf); buf=NULL; continue; } else if(cmd==CMD_FILE || cmd==CMD_ENC_FILE || cmd==CMD_METADATA || cmd==CMD_ENC_METADATA || cmd==CMD_EFS_FILE) { int forget=0; int64_t winattr=0; struct stat statbuf; char *extrameta=NULL; size_t elen=0; unsigned long long bytes=0; BFILE bfd; FILE *fp=NULL; sb.path=buf; buf=NULL; #ifdef HAVE_WIN32 if(win32_lstat(sb.path, &statbuf, &winattr)) #else if(lstat(sb.path, &statbuf)) #endif { logw(cntr, "Path has vanished: %s", sb.path); if(forget_file(&sb, cmd, cntr)) { ret=-1; quit++; } free_sbuf(&sb); continue; } if(conf->min_file_size && statbuf.st_size<(boffset_t)conf->min_file_size) { logw(cntr, "File size decreased below min_file_size after initial scan: %s", sb.path); forget++; } else if(conf->max_file_size && statbuf.st_size>(boffset_t)conf->max_file_size) { logw(cntr, "File size increased above max_file_size after initial scan: %s", sb.path); forget++; } if(!forget) { encode_stat(attribs, &statbuf, winattr); if(open_file_for_send(&bfd, &fp, sb.path, winattr, cntr)) forget++; } if(forget) { if(forget_file(&sb, cmd, cntr)) { ret=-1; quit++; } free_sbuf(&sb); continue; } if(cmd==CMD_METADATA || cmd==CMD_ENC_METADATA) { if(get_extrameta(sb.path, &statbuf, &extrameta, &elen, cntr)) { logw(cntr, "Meta data error for %s", sb.path); free_sbuf(&sb); close_file_for_send(&bfd, &fp); continue; } if(!extrameta) { logw(cntr, "No meta data after all: %s", sb.path); free_sbuf(&sb); close_file_for_send(&bfd, &fp); continue; } } if(cmd==CMD_FILE && sb.datapth) { unsigned long long sentbytes=0; // Need to do sig/delta stuff. if(async_write_str(CMD_DATAPTH, sb.datapth) || async_write_str(CMD_STAT, attribs) || async_write_str(CMD_FILE, sb.path) || load_signature_and_send_delta( &bfd, fp, &bytes, &sentbytes, cntr)) { logp("error in sig/delta for %s (%s)\n", sb.path, sb.datapth); ret=-1; quit++; } else { do_filecounter(cntr, CMD_FILE_CHANGED, 1); do_filecounter_bytes(cntr, bytes); do_filecounter_sentbytes(cntr, sentbytes); } } else { //logp("need to send whole file: %s\n", // sb.path); // send the whole file. if(async_write_str(CMD_STAT, attribs) || async_write_str(cmd, sb.path) || send_whole_file_w(cmd, sb.path, NULL, 0, &bytes, conf->encryption_password, cntr, conf->compression, &bfd, fp, extrameta, elen)) { ret=-1; quit++; } else { do_filecounter(cntr, cmd, 1); do_filecounter_bytes(cntr, bytes); do_filecounter_sentbytes(cntr, bytes); } } close_file_for_send(&bfd, &fp); free_sbuf(&sb); if(extrameta) free(extrameta); } else if(cmd==CMD_WARNING) { do_filecounter(cntr, cmd, 0); free(buf); buf=NULL; } else if(cmd==CMD_GEN && !strcmp(buf, "backupphase2end")) { if(async_write_str(CMD_GEN, "okbackupphase2end")) ret=-1; quit++; } else { logp("unexpected cmd from server: %c %s\n", cmd, buf); ret=-1; quit++; free(buf); buf=NULL; } } } return ret; }
int authorise_server(struct config *conf, char **client, char **cversion, struct config *cconf, struct cntr *p1cntr) { char cmd; char *cp=NULL; size_t len=0; char *buf=NULL; char *password=NULL; char whoareyou[256]=""; if(async_read(&cmd, &buf, &len)) { logp("unable to read initial message\n"); return -1; } if(cmd!=CMD_GEN || strncmp(buf, "hello", strlen("hello"))) { logp("unexpected command given: %c %s\n", cmd, buf); free(buf); return -1; } // String may look like... // "hello" // "hello:(version)" // (version) is a version number if((cp=strchr(buf, ':'))) { cp++; if(cp) *cversion=strdup(cp); } free(buf); buf=NULL; snprintf(whoareyou, sizeof(whoareyou), "whoareyou"); if(*cversion) { long min_ver=0; long cli_ver=0; if((min_ver=version_to_long("1.3.2"))<0 || (cli_ver=version_to_long(*cversion))<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", VERSION); } async_write_str(CMD_GEN, whoareyou); if(async_read(&cmd, &buf, &len) || !len) { logp("unable to get client name\n"); if(*cversion) free(*cversion); *cversion=NULL; return -1; } *client=buf; buf=NULL; async_write_str(CMD_GEN, "okpassword"); if(async_read(&cmd, &buf, &len) || !len) { logp("unable to get password for client %s\n", *client); if(*client) free(*client); *client=NULL; if(*cversion) free(*cversion); *cversion=NULL; free(buf); buf=NULL; return -1; } password=buf; buf=NULL; if(check_client_and_password(conf, *client, password, cconf)) { if(*client) free(*client); *client=NULL; if(*cversion) free(*cversion); *cversion=NULL; free(password); password=NULL; return -1; } version_warn(p1cntr, *client, *cversion); logp("auth ok for client: %s\n", *client); if(password) free(password); async_write_str(CMD_GEN, "ok"); return 0; }
int do_restore_server(const char *basedir, enum action act, const char *client, int srestore, char **dir_for_notify, struct cntr *p1cntr, struct cntr *cntr, struct config *cconf) { int a=0; int i=0; int ret=0; int found=0; struct bu *arr=NULL; unsigned long index=0; char *tmppath1=NULL; char *tmppath2=NULL; regex_t *regex=NULL; logp("in do_restore\n"); if(compile_regex(®ex, cconf->regex)) return -1; if(!(tmppath1=prepend_s(basedir, "tmp1", strlen("tmp1"))) || !(tmppath2=prepend_s(basedir, "tmp2", strlen("tmp2")))) { if(tmppath1) free(tmppath1); if(regex) { regfree(regex); free(regex); } return -1; } if(get_current_backups(basedir, &arr, &a, 1)) { if(tmppath1) free(tmppath1); if(tmppath2) free(tmppath2); if(regex) { regfree(regex); free(regex); } return -1; } if(!(index=strtoul(cconf->backup, NULL, 10)) && a>0) { // No backup specified, do the most recent. ret=restore_manifest(arr, a, a-1, tmppath1, tmppath2, regex, srestore, act, client, dir_for_notify, p1cntr, cntr, cconf); found=TRUE; } if(!found) for(i=0; i<a; i++) { if(!strcmp(arr[i].timestamp, cconf->backup) || arr[i].index==index) { found=TRUE; //logp("got: %s\n", arr[i].path); ret|=restore_manifest(arr, a, i, tmppath1, tmppath2, regex, srestore, act, client, dir_for_notify, p1cntr, cntr, cconf); break; } } free_current_backups(&arr, a); if(!found) { logp("backup not found\n"); async_write_str(CMD_ERROR, "backup not found"); ret=-1; } if(tmppath1) { unlink(tmppath1); free(tmppath1); } if(tmppath2) { unlink(tmppath2); free(tmppath2); } if(regex) { regfree(regex); free(regex); } return ret; }
int do_list_server(const char *basedir, const char *backup, const char *listregex, const char *browsedir, const char *client, struct cntr *p1cntr, struct cntr *cntr) { int a=0; int i=0; int ret=0; int found=0; struct bu *arr=NULL; unsigned long index=0; regex_t *regex=NULL; logp("in do_list\n"); if(compile_regex(®ex, listregex)) return -1; if(get_current_backups(basedir, &arr, &a, 1)) { if(regex) { regfree(regex); free(regex); } return -1; } write_status(client, STATUS_LISTING, NULL, p1cntr, cntr); if(backup && *backup) index=strtoul(backup, NULL, 10); for(i=0; i<a; i++) { // Search all backups for things matching the regex. if(listregex && backup && *backup=='a') { found=TRUE; async_write(CMD_TIMESTAMP, arr[i].timestamp, strlen(arr[i].timestamp)); ret+=list_manifest(arr[i].path, regex, browsedir, client, p1cntr, cntr); } // Search or list a particular backup. else if(backup && *backup) { if(!found && (!strcmp(arr[i].timestamp, backup) || arr[i].index==index)) { found=TRUE; async_write(CMD_TIMESTAMP, arr[i].timestamp, strlen(arr[i].timestamp)); ret=list_manifest(arr[i].path, regex, browsedir, client, p1cntr, cntr); } } // List the backups. else { found=TRUE; async_write(CMD_TIMESTAMP, arr[i].timestamp, strlen(arr[i].timestamp)); } } free_current_backups(&arr, a); if(backup && *backup && !found) { async_write_str(CMD_ERROR, "backup not found"); ret=-1; } if(regex) { regfree(regex); free(regex); } return ret; }
static int do_backup_phase2_client(struct config *conf, int resume, struct cntr *p1cntr, struct cntr *cntr) { int ret=0; int quit=0; char cmd; char *buf=NULL; size_t len=0; char attribs[MAXSTRING]; // For efficiency, open Windows files for the VSS data, and do not // close them until another time around the loop, when the actual // data is read. BFILE bfd; // Windows VSS headers tell us how much file // data to expect. size_t datalen=0; #ifdef HAVE_WIN32 binit(&bfd, 0); #endif struct sbuf sb; init_sbuf(&sb); if(!resume) { // Only do this bit if the server did not tell us to resume. if(async_write_str(CMD_GEN, "backupphase2") || async_read_expect(CMD_GEN, "ok")) return -1; } else if(conf->send_client_counters) { // On resume, the server might update the client with the // counters. if(recv_counters(p1cntr, cntr)) return -1; } while(!quit) { if(async_read(&cmd, &buf, &len)) { ret=-1; quit++; } else if(buf) { //logp("now: %c:%s\n", cmd, buf); if(cmd==CMD_DATAPTH) { sb.datapth=buf; buf=NULL; continue; } else if(cmd==CMD_STAT) { // Ignore the stat data - we will fill it // in again. Some time may have passed by now, // and it is best to make it as fresh as // possible. free(buf); buf=NULL; continue; } else if(cmd==CMD_FILE || cmd==CMD_ENC_FILE || cmd==CMD_METADATA || cmd==CMD_ENC_METADATA || cmd==CMD_VSS || cmd==CMD_ENC_VSS || cmd==CMD_VSS_T || cmd==CMD_ENC_VSS_T || cmd==CMD_EFS_FILE) { int forget=0; int64_t winattr=0; struct stat statbuf; char *extrameta=NULL; size_t elen=0; unsigned long long bytes=0; FILE *fp=NULL; int compression=conf->compression; sb.path=buf; buf=NULL; #ifdef HAVE_WIN32 if(win32_lstat(sb.path, &statbuf, &winattr)) #else if(lstat(sb.path, &statbuf)) #endif { logw(cntr, "Path has vanished: %s", sb.path); if(forget_file(&sb, cmd, cntr)) { ret=-1; quit++; } free_sbuf(&sb); continue; } if(conf->min_file_size && statbuf.st_size< (boffset_t)conf->min_file_size && (cmd==CMD_FILE || cmd==CMD_ENC_FILE || cmd==CMD_EFS_FILE)) { logw(cntr, "File size decreased below min_file_size after initial scan: %c:%s", cmd, sb.path); forget++; } else if(conf->max_file_size && statbuf.st_size> (boffset_t)conf->max_file_size && (cmd==CMD_FILE || cmd==CMD_ENC_FILE || cmd==CMD_EFS_FILE)) { logw(cntr, "File size increased above max_file_size after initial scan: %c:%s", cmd, sb.path); forget++; } if(!forget) { compression=in_exclude_comp(conf->excom, conf->excmcount, sb.path, conf->compression); encode_stat(attribs, &statbuf, winattr, compression); if(open_file_for_send( #ifdef HAVE_WIN32 &bfd, NULL, #else NULL, &fp, #endif sb.path, winattr, &datalen, cntr)) forget++; } if(forget) { if(forget_file(&sb, cmd, cntr)) { ret=-1; quit++; } free_sbuf(&sb); continue; } if(cmd==CMD_METADATA || cmd==CMD_ENC_METADATA || cmd==CMD_VSS || cmd==CMD_ENC_VSS #ifdef HAVE_WIN32 || conf->strip_vss #endif ) { if(get_extrameta( #ifdef HAVE_WIN32 &bfd, #else NULL, #endif sb.path, &statbuf, &extrameta, &elen, winattr, cntr, &datalen)) { logw(cntr, "Meta data error for %s", sb.path); free_sbuf(&sb); close_file_for_send(&bfd, &fp); continue; } if(extrameta) { #ifdef HAVE_WIN32 if(conf->strip_vss) { free(extrameta); extrameta=NULL; elen=0; } #endif } else { logw(cntr, "No meta data after all: %s", sb.path); free_sbuf(&sb); close_file_for_send(&bfd, &fp); continue; } } if(cmd==CMD_FILE && sb.datapth) { unsigned long long sentbytes=0; // Need to do sig/delta stuff. if(async_write_str(CMD_DATAPTH, sb.datapth) || async_write_str(CMD_STAT, attribs) || async_write_str(CMD_FILE, sb.path) || load_signature_and_send_delta( &bfd, fp, &bytes, &sentbytes, cntr, datalen)) { logp("error in sig/delta for %s (%s)\n", sb.path, sb.datapth); ret=-1; quit++; } else { do_filecounter(cntr, CMD_FILE_CHANGED, 1); do_filecounter_bytes(cntr, bytes); do_filecounter_sentbytes(cntr, sentbytes); } } else { //logp("need to send whole file: %s\n", // sb.path); // send the whole file. if((async_write_str(CMD_STAT, attribs) || async_write_str(cmd, sb.path)) || send_whole_file_w(cmd, sb.path, NULL, 0, &bytes, conf->encryption_password, cntr, compression, &bfd, fp, extrameta, elen, datalen)) { ret=-1; quit++; } else { do_filecounter(cntr, cmd, 1); do_filecounter_bytes(cntr, bytes); do_filecounter_sentbytes(cntr, bytes); } } #ifdef HAVE_WIN32 // If using Windows do not close bfd - it needs // to stay open to read VSS/file data/VSS. // It will get closed either when given a // different file path, or when this function // exits. //if(cmd!=CMD_VSS // && cmd!=CMD_ENC_VSS) // close_file_for_send(&bfd, NULL); #else close_file_for_send(NULL, &fp); #endif free_sbuf(&sb); if(extrameta) free(extrameta); } else if(cmd==CMD_WARNING) { do_filecounter(cntr, cmd, 0); free(buf); buf=NULL; } else if(cmd==CMD_GEN && !strcmp(buf, "backupphase2end")) { if(async_write_str(CMD_GEN, "okbackupphase2end")) ret=-1; quit++; } else { logp("unexpected cmd from server: %c %s\n", cmd, buf); ret=-1; quit++; free(buf); buf=NULL; } } } #ifdef HAVE_WIN32 // It is possible for a bfd to still be open. close_file_for_send(&bfd, NULL); #endif return ret; }
int transfer_gzfile_in(struct sbuf *sb, const char *path, BFILE *bfd, FILE *fp, unsigned long long *rcvd, unsigned long long *sent, const char *encpassword, int enccompressed, struct cntr *cntr, char **metadata) { char cmd; char *buf=NULL; size_t len=0; int quit=0; int ret=-1; unsigned char out[ZCHUNK]; size_t doutlen=0; //unsigned char doutbuf[1000+EVP_MAX_BLOCK_LENGTH]; unsigned char doutbuf[ZCHUNK-EVP_MAX_BLOCK_LENGTH]; z_stream zstrm; EVP_CIPHER_CTX *enc_ctx=NULL; // Checksum stuff //MD5_CTX md5; //unsigned char checksum[MD5_DIGEST_LENGTH+1]; //logp("in transfer_gzfile_in\n"); #ifdef HAVE_WIN32 if(sb && sb->cmd==CMD_EFS_FILE) return transfer_efs_in(bfd, rcvd, sent, cntr); #endif //if(!MD5_Init(&md5)) //{ // logp("MD5_Init() failed"); // return -1; //} zstrm.zalloc=Z_NULL; zstrm.zfree=Z_NULL; zstrm.opaque=Z_NULL; zstrm.avail_in=0; zstrm.next_in=Z_NULL; if(inflateInit2(&zstrm, (15+16))) { logp("unable to init inflate\n"); return -1; } if(encpassword && !(enc_ctx=enc_setup(0, encpassword))) { inflateEnd(&zstrm); return -1; } while(!quit) { if(async_read(&cmd, &buf, &len)) { if(enc_ctx) { EVP_CIPHER_CTX_cleanup(enc_ctx); free(enc_ctx); } inflateEnd(&zstrm); return -1; } (*rcvd)+=len; //logp("transfer in: %c:%s\n", cmd, buf); switch(cmd) { case CMD_APPEND: // append if(!fp && !bfd && !metadata) { logp("given append, but no file or metadata to write to\n"); async_write_str(CMD_ERROR, "append with no file or metadata"); quit++; ret=-1; } else { size_t lentouse; unsigned char *buftouse=NULL; /* if(!MD5_Update(&md5, buf, len)) { logp("MD5 update enc error\n"); quit++; ret=-1; break; } */ // If doing decryption, it needs // to be done before uncompressing. if(enc_ctx) { // updating our checksum needs to // be done first /* if(!MD5_Update(&md5, buf, len)) { logp("MD5 update enc error\n"); quit++; ret=-1; break; } else */ if(!EVP_CipherUpdate(enc_ctx, doutbuf, (int *)&doutlen, (unsigned char *)buf, len)) { logp("Decryption error\n"); quit++; ret=-1; break; } if(!doutlen) break; lentouse=doutlen; buftouse=doutbuf; } else { lentouse=len; buftouse=(unsigned char *)buf; } //logp("want to write: %d\n", zstrm.avail_in); if(do_inflate(&zstrm, bfd, fp, out, buftouse, lentouse, metadata, encpassword, enccompressed, sent)) { ret=-1; quit++; break; } } break; case CMD_END_FILE: // finish up if(enc_ctx) { if(!EVP_CipherFinal_ex(enc_ctx, doutbuf, (int *)&doutlen)) { logp("Decryption failure at the end.\n"); ret=-1; quit++; break; } if(doutlen && do_inflate(&zstrm, bfd, fp, out, doutbuf, doutlen, metadata, encpassword, enccompressed, sent)) { ret=-1; quit++; break; } } /* if(MD5_Final(checksum, &md5)) { char *oldsum=NULL; const char *newsum=NULL; if((oldsum=strchr(buf, ':'))) { oldsum++; newsum=get_checksum_str(checksum); // log if the checksum differed if(strcmp(newsum, oldsum)) logw(cntr, "md5sum for '%s' did not match! (%s!=%s)\n", path, newsum, oldsum); } } else { logp("MD5_Final() failed\n"); } */ quit++; ret=0; break; case CMD_WARNING: logp("WARNING: %s\n", buf); do_filecounter(cntr, cmd, 0); break; default: logp("unknown append cmd: %c\n", cmd); quit++; ret=-1; break; } if(buf) free(buf); buf=NULL; } inflateEnd(&zstrm); if(enc_ctx) { EVP_CIPHER_CTX_cleanup(enc_ctx); free(enc_ctx); } if(ret) logp("transfer file returning: %d\n", ret); return ret; }
// returns 1 for finished ok. static int do_stuff_to_receive(struct sbuf *rb, FILE *p2fp, const char *datadirtmp, struct dpth *dpth, const char *working, char **last_requested, const char *deltmppath, struct cntr *cntr, struct config *cconf) { int ret=0; char rcmd; size_t rlen=0; size_t wlen=0; char *rbuf=NULL; // This also attempts to write anything in the write buffer. if(async_rw(&rcmd, &rbuf, &rlen, '\0', NULL, &wlen)) { logp("error in async_rw\n"); return -1; } if(rbuf) { if(rcmd==CMD_WARNING) { logp("WARNING: %s\n", rbuf); do_filecounter(cntr, rcmd, 0); } else if(rb->fp || rb->zp) { // Currently writing a file (or meta data) if(rcmd==CMD_APPEND) { int app; //logp("rlen: %d\n", rlen); if((rb->zp && (app=gzwrite(rb->zp, rbuf, rlen))<=0) || (rb->fp && (app=fwrite(rbuf, 1, rlen, rb->fp))<=0)) { logp("error when appending: %d\n", app); async_write_str(CMD_ERROR, "write failed"); ret=-1; } do_filecounter_recvbytes(cntr, rlen); } else if(rcmd==CMD_END_FILE) { // Finished the file. // Write it to the phase2 file, and free the // buffers. if(close_fp(&(rb->fp))) { logp("error closing delta for %s in receive\n", rb->path); ret=-1; } if(gzclose_fp(&(rb->zp))) { logp("error gzclosing delta for %s in receive\n", rb->path); ret=-1; } rb->endfile=rbuf; rb->elen=rlen; rbuf=NULL; if(!ret && rb->receivedelta && finish_delta(rb, working, deltmppath)) ret=-1; else if(!ret) { if(sbuf_to_manifest(rb, p2fp, NULL)) ret=-1; else { char cmd=rb->cmd; if(rb->receivedelta) do_filecounter_changed(cntr, cmd); else do_filecounter(cntr, cmd, 0); if(*last_requested && !strcmp(rb->path, *last_requested)) { free(*last_requested); *last_requested=NULL; } } } if(!ret) { char *cp=NULL; cp=strchr(rb->endfile, ':'); if(rb->endfile) do_filecounter_bytes(cntr, strtoull(rb->endfile, NULL, 10)); if(cp) { // checksum stuff goes here } } free_sbuf(rb); } else { logp("unexpected cmd from client while writing file: %c %s\n", rcmd, rbuf); ret=-1; } } // Otherwise, expecting to be told of a file to save. else if(rcmd==CMD_DATAPTH) { rb->datapth=rbuf; rbuf=NULL; } else if(rcmd==CMD_STAT) { rb->statbuf=rbuf; rb->slen=rlen; rbuf=NULL; } else if(filedata(rcmd)) { rb->cmd=rcmd; rb->plen=rlen; rb->path=rbuf; rbuf=NULL; if(rb->datapth) { // Receiving a delta. if(start_to_receive_delta(rb, working, deltmppath, cconf)) { logp("error in start_to_receive_delta\n"); ret=-1; } } else { // Receiving a whole new file. if(start_to_receive_new_file(rb, datadirtmp, dpth, cntr, cconf)) { logp("error in start_to_receive_new_file\n"); ret=-1; } } } else if(rcmd==CMD_GEN && !strcmp(rbuf, "okbackupphase2end")) { ret=1; //logp("got okbackupphase2end\n"); } else if(rcmd==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, *last_requested)) { free(*last_requested); *last_requested=NULL; } } else { logp("unexpected cmd from client while expecting a file: %c %s\n", rcmd, rbuf); ret=-1; } if(rbuf) { free(rbuf); rbuf=NULL; } } //logp("returning: %d\n", ret); return ret; }
int do_restore_server(const char *basedir, const char *backup, const char *restoreregex, enum action act, const char *client, struct cntr *p1cntr, struct cntr *cntr, struct config *cconf) { int a=0; int i=0; int ret=0; int found=0; struct bu *arr=NULL; unsigned long index=0; char *tmppath1=NULL; char *tmppath2=NULL; regex_t *regex=NULL; bool all=FALSE; logp("in do_restore\n"); if(compile_regex(®ex, restoreregex)) return -1; if(!(tmppath1=prepend_s(basedir, "tmp1", strlen("tmp1"))) || !(tmppath2=prepend_s(basedir, "tmp2", strlen("tmp2")))) { if(tmppath1) free(tmppath1); if(regex) { regfree(regex); free(regex); } return -1; } if(get_current_backups(basedir, &arr, &a, 1)) { if(tmppath1) free(tmppath1); if(tmppath2) free(tmppath2); if(regex) { regfree(regex); free(regex); } return -1; } if(backup && *backup=='a') { all=TRUE; } else if(!(index=strtoul(backup, NULL, 10)) && a>0) { // No backup specified, do the most recent. ret=restore_manifest(arr, a, a-1, tmppath1, tmppath2, regex, act, client, p1cntr, cntr, cconf, all); found=TRUE; } if(!found) for(i=0; i<a; i++) { if(all || !strcmp(arr[i].timestamp, backup) || arr[i].index==index) { found=TRUE; //logp("got: %s\n", arr[i].path); ret|=restore_manifest(arr, a, i, tmppath1, tmppath2, regex, act, client, p1cntr, cntr, cconf, all); if(!all) break; } } // If doing all backups, send restore end. if(!ret && all && found) ret=do_restore_end(act, cntr); free_current_backups(&arr, a); if(!found) { logp("backup not found\n"); async_write_str(CMD_ERROR, "backup not found"); ret=-1; } if(tmppath1) { unlink(tmppath1); free(tmppath1); } if(tmppath2) { unlink(tmppath2); free(tmppath2); } if(regex) { regfree(regex); free(regex); } return ret; }
int send_file(FF_PKT *ff, bool top_level, struct config *conf, struct cntr *p1cntr) { char msg[128]=""; char attribs[MAXSTRING]; if(!file_is_included(conf->incexcdir, conf->iecount, conf->incext, conf->incount, conf->excext, conf->excount, conf->increg, conf->ircount, conf->excreg, conf->ercount, ff->fname, top_level)) return 0; #ifdef HAVE_WIN32 // Useful Windows attributes debug /* printf("\n%llu", ff->winattr); printf("\n%s\n", ff->fname); if(ff->winattr & FILE_ATTRIBUTE_READONLY) printf("readonly\n"); if(ff->winattr & FILE_ATTRIBUTE_HIDDEN) printf("hidden\n"); if(ff->winattr & FILE_ATTRIBUTE_SYSTEM) printf("system\n"); if(ff->winattr & FILE_ATTRIBUTE_DIRECTORY) printf("directory\n"); if(ff->winattr & FILE_ATTRIBUTE_ARCHIVE) printf("archive\n"); if(ff->winattr & FILE_ATTRIBUTE_DEVICE) printf("device\n"); if(ff->winattr & FILE_ATTRIBUTE_NORMAL) printf("normal\n"); if(ff->winattr & FILE_ATTRIBUTE_TEMPORARY) printf("temporary\n"); if(ff->winattr & FILE_ATTRIBUTE_SPARSE_FILE) printf("sparse\n"); if(ff->winattr & FILE_ATTRIBUTE_REPARSE_POINT) printf("reparse\n"); if(ff->winattr & FILE_ATTRIBUTE_COMPRESSED) printf("compressed\n"); if(ff->winattr & FILE_ATTRIBUTE_OFFLINE) printf("offline\n"); if(ff->winattr & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) printf("notcont\n"); if(ff->winattr & FILE_ATTRIBUTE_ENCRYPTED) printf("encrypted\n"); if(ff->winattr & FILE_ATTRIBUTE_VIRTUAL) printf("virtual\n"); */ if(ff->winattr & FILE_ATTRIBUTE_ENCRYPTED) { // if(ff->type!=FT_DIREND) // logw(p1cntr, "EFS not yet supported: %s", ff->fname); // return 0; if(ff->type==FT_REGE || ff->type==FT_REG || ff->type==FT_DIRBEGIN) { encode_stat(attribs, &ff->statp, ff->winattr, conf->compression); if(async_write_str(CMD_STAT, attribs) || async_write_str(CMD_EFS_FILE, ff->fname)) return -1; do_filecounter(p1cntr, CMD_EFS_FILE, 1); if(ff->type==FT_REG) do_filecounter_bytes(p1cntr, (unsigned long long)ff->statp.st_size); return 0; } else if(ff->type==FT_DIREND) return 0; else { // Hopefully, here is never reached. logw(p1cntr, "EFS type %d not yet supported: %s", ff->type, ff->fname); return 0; } } #endif //logp("%d: %s\n", ff->type, ff->fname); switch (ff->type) { case FT_LNKSAVED: //printf("Lnka: %s -> %s\n", ff->fname, ff->link); encode_stat(attribs, &ff->statp, ff->winattr, conf->compression); if(async_write_str(CMD_STAT, attribs) || async_write_str(CMD_HARD_LINK, ff->fname) || async_write_str(CMD_HARD_LINK, ff->link)) return -1; do_filecounter(p1cntr, CMD_HARD_LINK, 1); // At least FreeBSD 8.2 can have different xattrs on hard links. if(maybe_send_extrameta(ff->fname, CMD_HARD_LINK, attribs, p1cntr)) return -1; break; case FT_FIFO: case FT_REGE: case FT_REG: encode_stat(attribs, &ff->statp, ff->winattr, in_exclude_comp(conf->excom, conf->excmcount, ff->fname, conf->compression)); if(async_write_str(CMD_STAT, attribs) || async_write_str(filesymbol, ff->fname)) return -1; do_filecounter(p1cntr, filesymbol, 1); if(ff->type==FT_REG) do_filecounter_bytes(p1cntr, (unsigned long long)ff->statp.st_size); if(maybe_send_extrameta(ff->fname, filesymbol, attribs, p1cntr)) return -1; break; case FT_LNK: //printf("link: %s -> %s\n", ff->fname, ff->link); encode_stat(attribs, &ff->statp, ff->winattr, conf->compression); if(async_write_str(CMD_STAT, attribs) || async_write_str(CMD_SOFT_LINK, ff->fname) || async_write_str(CMD_SOFT_LINK, ff->link)) return -1; do_filecounter(p1cntr, CMD_SOFT_LINK, 1); if(maybe_send_extrameta(ff->fname, CMD_SOFT_LINK, attribs, p1cntr)) return -1; break; case FT_DIREND: return 0; case FT_NOFSCHG: case FT_DIRBEGIN: case FT_REPARSE: case FT_JUNCTION: { char errmsg[100] = ""; if (ff->type == FT_NOFSCHG) snprintf(errmsg, sizeof(errmsg), _("\t[will not descend: file system change not allowed]")); if(*errmsg) { snprintf(msg, sizeof(msg), "%s%s%s\n", "Dir: ", ff->fname, errmsg); logw(p1cntr, "%s", msg); } else { encode_stat(attribs, &ff->statp, ff->winattr, conf->compression); if(async_write_str(CMD_STAT, attribs)) return -1; #if defined(WIN32_VSS) if(async_write_str(filesymbol, ff->fname)) return -1; do_filecounter(p1cntr, filesymbol, 1); #else if(async_write_str(CMD_DIRECTORY, ff->fname)) return -1; do_filecounter(p1cntr, CMD_DIRECTORY, 1); if(maybe_send_extrameta(ff->fname, CMD_DIRECTORY, attribs, p1cntr)) return -1; #endif } } break; case FT_SPEC: // special file - fifo, socket, device node... encode_stat(attribs, &ff->statp, ff->winattr, conf->compression); if(async_write_str(CMD_STAT, attribs) || async_write_str(CMD_SPECIAL, ff->fname)) return -1; do_filecounter(p1cntr, CMD_SPECIAL, 1); if(maybe_send_extrameta(ff->fname, CMD_SPECIAL, attribs, p1cntr)) return -1; break; case FT_NOACCESS: logw(p1cntr, _("Err: Could not access %s: %s"), ff->fname, strerror(errno)); break; case FT_NOFOLLOW: logw(p1cntr, _("Err: Could not follow ff->link %s: %s"), ff->fname, strerror(errno)); break; case FT_NOSTAT: logw(p1cntr, _("Err: Could not stat %s: %s"), ff->fname, strerror(errno)); break; case FT_NOCHG: logw(p1cntr, _("Skip: File not saved. No change. %s"), ff->fname); break; case FT_ISARCH: logw(p1cntr, _("Err: Attempt to backup archive. Not saved. %s"), ff->fname); break; case FT_NOOPEN: logw(p1cntr, _("Err: Could not open directory %s: %s"), ff->fname, strerror(errno)); break; case FT_RAW: logw(p1cntr, _("Err: Raw partition: %s"), ff->fname); break; default: logw(p1cntr, _("Err: Unknown file ff->type %d: %s"), ff->type, ff->fname); break; } return 0; }
int backup_phase2_server(gzFile *cmanfp, const char *phase1data, const char *phase2data, const char *unchangeddata, const char *datadirtmp, struct dpth *dpth, const char *currentdata, const char *working, const char *client, struct cntr *p1cntr, int resume, struct cntr *cntr, struct config *cconf) { int ars=0; int ret=0; gzFile p1zp=NULL; char *deltmppath=NULL; char *last_requested=NULL; // Where to write phase2data. // Data is not getting written to a compressed file. // This is important for recovery if the power goes. FILE *p2fp=NULL; // unchanged data FILE *ucfp=NULL; int resume_partial=resume; struct sbuf cb; // file list in current manifest struct sbuf p1b; // file list from client struct sbuf rb; // receiving file from client init_sbuf(&cb); init_sbuf(&p1b); init_sbuf(&rb); if(!(p1zp=gzopen_file(phase1data, "rb"))) goto error; // Open in read+write mode, so that they can be read through if // we need to resume. // First, open them in a+ mode, so that they will be created if they // do not exist. if(!(ucfp=open_file(unchangeddata, "a+b"))) goto error; if(!(p2fp=open_file(phase2data, "a+b"))) goto error; close_fp(&ucfp); close_fp(&p2fp); if(!(ucfp=open_file(unchangeddata, "r+b"))) goto error; if(!(p2fp=open_file(phase2data, "r+b"))) goto error; if(resume && do_resume(p1zp, p2fp, ucfp, dpth, cconf, client, p1cntr, cntr)) goto error; logp("Begin phase2 (receive file data)\n"); if(!(deltmppath=prepend_s(working, "delta.tmp", strlen("delta.tmp")))) goto error; while(1) { int sts=0; // logp("in loop, %s %s %c\n", // *cmanfp?"got cmanfp":"no cmanfp", // rb.path?:"no rb.path", rb.path?'X':rb.cmd); if(rb.path) write_status(client, STATUS_BACKUP, rb.path, p1cntr, cntr); else write_status(client, STATUS_BACKUP, p1b.path, p1cntr, cntr); if((last_requested || !p1zp || writebuflen) && (ars=do_stuff_to_receive(&rb, p2fp, datadirtmp, dpth, working, &last_requested, deltmppath, cntr, cconf))) { if(ars<0) goto error; // 1 means ok. break; } if((sts=do_stuff_to_send(&p1b, &last_requested))<0) goto error; if(!sts && p1zp) { free_sbuf(&p1b); if((ars=sbuf_fill_phase1(NULL, p1zp, &p1b, cntr))) { if(ars<0) goto error; // ars==1 means it ended ok. gzclose_fp(&p1zp); //logp("ended OK - write phase2end"); if(async_write_str(CMD_GEN, "backupphase2end")) goto error; } //logp("check: %s\n", p1b.path); if(!*cmanfp) { // No old manifest, need to ask for a new file. //logp("no cmanfp\n"); if(process_new(&p1b, ucfp, currentdata, datadirtmp, deltmppath, dpth, &resume_partial, cntr, cconf)) goto error; } else { // Have an old manifest, look for it there. // Might already have it, or be ahead in the old // manifest. if(cb.path) { if((ars=maybe_process_file(&cb, &p1b, ucfp, currentdata, datadirtmp, deltmppath, dpth, &resume_partial, cntr, cconf))) { if(ars<0) goto error; // Do not free it - need to send stuff. continue; } //free_sbuf(&p1b); } while(*cmanfp) { free_sbuf(&cb); if((ars=sbuf_fill(NULL, *cmanfp, &cb, cntr))) { // ars==1 means it ended ok. if(ars<0) goto error; gzclose_fp(cmanfp); //logp("ran out of current manifest\n"); if(process_new(&p1b, ucfp, currentdata, datadirtmp, deltmppath, dpth, &resume_partial, cntr, cconf)) goto error; break; } //logp("against: %s\n", cb.path); if((ars=maybe_process_file(&cb, &p1b, ucfp, currentdata, datadirtmp, deltmppath, dpth, &resume_partial, cntr, cconf))) { if(ars<0) goto error; // Do not free it - need to send stuff. break; } } } } } goto end; error: ret=-1; end: if(close_fp(&p2fp)) { logp("error closing %s in backup_phase2_server\n", phase2data); ret=-1; } if(close_fp(&ucfp)) { logp("error closing %s in backup_phase2_server\n", unchangeddata); ret=-1; } free(deltmppath); free_sbuf(&cb); free_sbuf(&p1b); free_sbuf(&rb); gzclose_fp(&p1zp); if(!ret) unlink(phase1data); logp("End phase2 (receive file data)\n"); return ret; }