static #endif int forward_before_entry(struct manio *manio, struct iobuf *target, struct cntr *cntr, struct dpth *dpth, enum protocol protocol, man_off_t **pos) { int ars=0; struct sbuf *sb=NULL; if(!(sb=sbuf_alloc(protocol))) goto error; man_off_t_free(pos); if(!(*pos=manio_tell(manio))) { logp("Could not manio_tell first pos in %s(): %s\n", __func__, strerror(errno)); goto error; } while(1) { if(sb->endfile.buf || (sb->path.buf && !sbuf_is_filedata(sb))) { man_off_t_free(pos); if(!(*pos=manio_tell(manio))) { logp("Could not manio_tell pos in %s(): " "%s\n", __func__, strerror(errno)); goto error; } } sbuf_free_content(sb); ars=manio_read(manio, sb); if(dpth && set_higher_datapth(sb, dpth)) goto error; switch(ars) { case 0: break; case 1: sbuf_free(&sb); return 0; default: logp("Error in %s(), but continuing\n", __func__); // Treat error in unchanged manio as // OK - could have been a short write. sbuf_free(&sb); return 0; } if(iobuf_pathcmp(target, &sb->path)<=0) { sbuf_free(&sb); return 0; } if(cntr) { cntr_add_same(cntr, sb->path.cmd); if(sb->endfile.buf) { uint64_t e=strtoull(sb->endfile.buf, NULL, 10); cntr_add_bytes(cntr, e); } } } error: sbuf_free(&sb); man_off_t_free(pos); return -1; }
static #endif int get_last_good_entry(struct manio *manio, struct iobuf *result, struct cntr *cntr, struct dpth *dpth, enum protocol protocol, man_off_t **pos) { int ars=0; int got_vss_start=0; struct sbuf *sb=NULL; struct iobuf lastpath; if(!(sb=sbuf_alloc(protocol))) goto error; iobuf_init(&lastpath); man_off_t_free(pos); if(!(*pos=manio_tell(manio))) { logp("Could not manio_tell first pos in %s(): %s\n", __func__, strerror(errno)); goto error; } while(1) { if(sb->path.buf && !got_vss_start) { iobuf_free_content(&lastpath); iobuf_move(&lastpath, &sb->path); if(!sbuf_is_filedata(sb) && !sbuf_is_vssdata(sb)) { iobuf_free_content(result); iobuf_move(result, &lastpath); man_off_t_free(pos); if(!(*pos=manio_tell(manio))) { logp("Could not manio_tell pos in %s(): %s\n", __func__, strerror(errno)); goto error; } } } if(sb->endfile.buf && !got_vss_start) { iobuf_free_content(result); iobuf_move(result, &lastpath); man_off_t_free(pos); if(!(*pos=manio_tell(manio))) { logp("Could not manio_tell pos in %s(): %s\n", __func__, strerror(errno)); goto error; } } sbuf_free_content(sb); ars=manio_read(manio, sb); if(dpth && set_higher_datapth(sb, dpth)) goto error; switch(ars) { case 0: break; case 1: iobuf_free_content(&lastpath); sbuf_free(&sb); return 0; default: if(result->buf) logp("Error after %s in %s()\n", result->buf, __func__); // Treat error in changed manio as // OK - could have been a short write. iobuf_free_content(&lastpath); sbuf_free(&sb); return 0; } // Some hacks for split_vss. switch(sb->path.cmd) { case CMD_VSS: case CMD_ENC_VSS: got_vss_start=1; break; case CMD_VSS_T: case CMD_ENC_VSS_T: got_vss_start=0; break; case CMD_FILE: case CMD_ENC_FILE: if(S_ISDIR(sb->statp.st_mode)) got_vss_start=0; break; default: break; } if(cntr) { // FIX THIS: cannot distinguish between new and // changed files. cntr_add_changed(cntr, sb->path.cmd); if(sb->endfile.buf) { uint64_t e=strtoull(sb->endfile.buf, NULL, 10); cntr_add_bytes(cntr, e); } } } error: iobuf_free_content(&lastpath); sbuf_free(&sb); man_off_t_free(pos); return -1; }
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; }
int mkpath(char **rpath, const char *limit) { int ret=-1; char *cp=NULL; struct stat buf; #ifdef HAVE_WIN32 int windows_stupidity=0; #endif if((cp=strrchr(*rpath, '/'))) { *cp='\0'; #ifdef HAVE_WIN32 if(strlen(*rpath)==2 && (*rpath)[1]==':') { (*rpath)[1]='\0'; windows_stupidity++; } #endif if(!**rpath) { // We are down to the root, which is OK. } else if(lstat(*rpath, &buf)) { // does not exist - recurse further down, then come // back and try to mkdir it. if(mkpath(rpath, limit)) goto end; // Require that the user has set up the required paths // on the server correctly. I have seen problems with // part of the path being a temporary symlink that // gets replaced by burp with a proper directory. // Allow it to create the actual directory specified, // though. // That is, if limit is: // /var/spool/burp // and /var/spool exists, the directory will be // created. // If only /var exists, the directory will not be // created. // Caller can give limit=NULL to create the whole // path with no limit, as in a restore. if(limit && pathcmp(*rpath, limit)<0) { logp("will not mkdir %s\n", *rpath); goto end; } if(mkdir(*rpath, 0777)) { logp("could not mkdir %s: %s\n", *rpath, strerror(errno)); goto end; } } else if(S_ISDIR(buf.st_mode)) { // Is a directory - can put the slash back and return. } else if(S_ISLNK(buf.st_mode)) { // to help with the 'current' symlink } else { // something funny going on logp("warning: wanted '%s' to be a directory\n", *rpath); } } ret=0; end: #ifdef HAVE_WIN32 if(windows_stupidity) (*rpath)[1]=':'; #endif if(cp) *cp='/'; return ret; }
static int restore_file_or_get_meta(struct asfd *asfd, BFILE *bfd, struct sbuf *sb, const char *fname, enum action act, const char *encpassword, char **metadata, size_t *metalen, int vss_restore, struct conf *conf) { int ret=0; char *rpath=NULL; FILE *fp=NULL; if(act==ACTION_VERIFY) { cntr_add(conf->cntr, sb->path.cmd, 1); return 0; } if(build_path(fname, "", &rpath, NULL)) { char msg[256]=""; // failed - do a warning snprintf(msg, sizeof(msg), "build path failed: %s", fname); if(restore_interrupt(asfd, sb, msg, conf)) ret=-1; goto end; } #ifndef HAVE_WIN32 // We always want to open the file if it is on Windows. Otherwise, // only open it if we are not doing metadata. if(!metadata) { #endif if(open_for_restore(asfd, bfd, &fp, rpath, sb, vss_restore, conf)) { ret=-1; goto end; } #ifndef HAVE_WIN32 } #endif if(!ret) { int enccompressed=0; unsigned long long rcvdbytes=0; unsigned long long sentbytes=0; enccompressed=dpthl_is_compressed(sb->compression, sb->burp1->datapth.buf); /* printf("%s \n", fname); if(encpassword && !enccompressed) printf("encrypted and not compressed\n"); else if(!encpassword && enccompressed) printf("not encrypted and compressed\n"); else if(!encpassword && !enccompressed) printf("not encrypted and not compressed\n"); else if(encpassword && enccompressed) printf("encrypted and compressed\n"); */ if(metadata) { ret=transfer_gzfile_inl(asfd, sb, fname, NULL, NULL, &rcvdbytes, &sentbytes, encpassword, enccompressed, conf->cntr, metadata); *metalen=sentbytes; // skip setting cntr, as we do not actually // restore until a bit later goto end; } else { int c=0; #ifdef HAVE_WIN32 ret=transfer_gzfile_inl(asfd, sb, fname, bfd, NULL, &rcvdbytes, &sentbytes, encpassword, enccompressed, conf->cntr, NULL); //c=bclose(bfd); #else ret=transfer_gzfile_inl(asfd, sb, fname, NULL, fp, &rcvdbytes, &sentbytes, encpassword, enccompressed, conf->cntr, NULL); c=close_fp(&fp); #endif if(c) { logp("error closing %s in restore_file_or_get_meta\n", fname); ret=-1; } if(!ret) attribs_set(asfd, rpath, &(sb->statp), sb->winattr, conf); } if(ret) { char msg[256]=""; snprintf(msg, sizeof(msg), "Could not transfer file in: %s", rpath); if(restore_interrupt(asfd, sb, msg, conf)) ret=-1; goto end; } } if(!ret) cntr_add(conf->cntr, sb->path.cmd, 1); end: if(rpath) free(rpath); return ret; }
int recursive_delete(const char *d, const char *file, bool delfiles) { int n=-1; int ret=RECDEL_OK; struct dirent **dir; struct stat statp; char *directory=NULL; if(!file) { if(!(directory=prepend_s(d, "", 0))) return RECDEL_ERROR; } else if(!(directory=prepend_s(d, file, strlen(file)))) { log_out_of_memory(__FUNCTION__); return RECDEL_ERROR; } if(lstat(directory, &statp)) { // path does not exist. free(directory); return RECDEL_OK; } if((n=scandir(directory, &dir, 0, 0))<0) { logp("scandir %s: %s\n", directory, strerror(errno)); free(directory); return RECDEL_ERROR; } while(n--) { char *fullpath=NULL; if(dir[n]->d_ino==0 || !strcmp(dir[n]->d_name, ".") || !strcmp(dir[n]->d_name, "..")) { free(dir[n]); continue; } if(!(fullpath=prepend_s(directory, dir[n]->d_name, strlen(dir[n]->d_name)))) break; if(is_dir(fullpath)) { int r; if((r=recursive_delete(directory, dir[n]->d_name, delfiles))==RECDEL_ERROR) { free(fullpath); break; } // do not overwrite ret with OK if it previously // had ENTRIES_REMAINING if(r==RECDEL_ENTRIES_REMAINING) ret=r; } else if(delfiles) { if(unlink(fullpath)) { logp("unlink %s: %s\n", fullpath, strerror(errno)); ret=RECDEL_ENTRIES_REMAINING; } } else { ret=RECDEL_ENTRIES_REMAINING; } free(fullpath); free(dir[n]); } if(n>0) { ret=RECDEL_ERROR; for(; n>0; n--) free(dir[n]); } free(dir); if(ret==RECDEL_OK && rmdir(directory)) { logp("rmdir %s: %s\n", directory, strerror(errno)); ret=RECDEL_ERROR; } free(directory); return ret; }
static int load_signature_and_send_delta(struct asfd *asfd, BFILE *bfd, FILE *in, unsigned long long *bytes, unsigned long long *sentbytes, struct conf *conf, size_t datalen) { rs_job_t *job; rs_result r; rs_signature_t *sumset=NULL; unsigned char checksum[MD5_DIGEST_LENGTH+1]; rs_filebuf_t *infb=NULL; rs_filebuf_t *outfb=NULL; rs_buffers_t rsbuf; memset(&rsbuf, 0, sizeof(rsbuf)); if(load_signature(asfd, &sumset, conf)) return -1; if(!(job=rs_delta_begin(sumset))) { logp("could not start delta job.\n"); rs_free_sumset(sumset); return RS_IO_ERROR; } if(!(infb=rs_filebuf_new(asfd, bfd, in, NULL, -1, ASYNC_BUF_LEN, datalen, conf->cntr)) || !(outfb=rs_filebuf_new(asfd, NULL, NULL, NULL, asfd->fd, ASYNC_BUF_LEN, -1, conf->cntr))) { logp("could not rs_filebuf_new for delta\n"); if(infb) rs_filebuf_free(infb); return -1; } while(1) { rs_result delresult; delresult=rs_async(job, &rsbuf, infb, outfb); if(delresult==RS_DONE) { r=delresult; break; } else if(delresult==RS_BLOCKED || delresult==RS_RUNNING) { // Keep going } else { logp("error in rs_async for delta: %d\n", delresult); r=delresult; break; } // FIX ME: get it to read stuff (errors, for example) here too. if(asfd->as->rw(asfd->as)) return -1; } if(r!=RS_DONE) logp("delta loop returned: %d\n", r); if(r==RS_DONE) { *bytes=infb->bytes; *sentbytes=outfb->bytes; if(!MD5_Final(checksum, &(infb->md5))) { logp("MD5_Final() failed\n"); r=RS_IO_ERROR; } } rs_filebuf_free(infb); rs_filebuf_free(outfb); rs_job_free(job); rs_free_sumset(sumset); if(r==RS_DONE && write_endfile(asfd, *bytes, checksum)) return -1; return r; }
/* * The buf is already using BUF for an output buffer, and probably * contains some buffered output now. Write this out to F, and reset * the buffer cursor. */ rs_result rs_outfilebuf_drain(rs_job_t *job, rs_buffers_t *buf, void *opaque) { rs_filebuf_t *fb=(rs_filebuf_t *)opaque; int fd=fb->fd; size_t wlen; //logp("in rs_outfilebuf_drain\n"); /* This is only allowed if either the buf has no output buffer * yet, or that buffer could possibly be BUF. */ if(!buf->next_out) { if(buf->avail_out) { logp("buf->avail_out is %d in %s\n", buf->avail_out, __func__); return RS_IO_ERROR; } buf->next_out = fb->buf; buf->avail_out = fb->buf_len; return RS_DONE; } if(buf->avail_out > fb->buf_len) { logp("buf->avail_out > fb->buf_len (%d > %d) in %s\n", buf->avail_out, fb->buf_len, __func__); return RS_IO_ERROR; } if(buf->next_out < fb->buf) { logp("buf->next_out < fb->buf (%p < %p) in %s\n", buf->next_out, fb->buf, __func__); return RS_IO_ERROR; } if(buf->next_out > fb->buf + fb->buf_len) { logp("buf->next_out > fb->buf + fb->buf_len in %s\n", __func__); return RS_IO_ERROR; } if((wlen=buf->next_out-fb->buf)>0) { //logp("wlen: %d\n", wlen); if(fd>0) { size_t w=wlen; static struct iobuf *wbuf=NULL; if(!wbuf && !(wbuf=iobuf_alloc())) return RS_IO_ERROR; wbuf->cmd=CMD_APPEND; wbuf->buf=fb->buf; wbuf->len=wlen; switch(fb->asfd->append_all_to_write_buffer( fb->asfd, wbuf)) { case APPEND_OK: break; case APPEND_BLOCKED: return RS_BLOCKED; case APPEND_ERROR: default: return RS_IO_ERROR; } fb->bytes+=w; } else { size_t result=0; result=fzp_write(fb->fzp, fb->buf, wlen); if(wlen!=result) { logp("error draining buf to file: %s", strerror(errno)); return RS_IO_ERROR; } } } buf->next_out = fb->buf; buf->avail_out = fb->buf_len; return RS_DONE; }
// This deals with reading in the sparse index, as well as actual candidate // manifests. enum cand_ret candidate_load(struct candidate *candidate, const char *path, struct scores *scores) { enum cand_ret ret=CAND_RET_PERM; struct fzp *fzp=NULL; struct sbuf *sb=NULL; struct blk *blk=NULL; if(!(sb=sbuf_alloc(PROTO_2)) || !(blk=blk_alloc())) { ret=CAND_RET_PERM; goto error; } if(!(fzp=fzp_gzopen(path, "rb"))) { ret=CAND_RET_TEMP; goto error; } while(fzp) { sbuf_free_content(sb); switch(sbuf_fill_from_file(sb, fzp, blk, NULL)) { case 1: goto end; case -1: logp("Error reading %s in %s, pos %d\n", path, __func__, fzp_tell(fzp)); ret=CAND_RET_TEMP; goto error; } if(blk_fingerprint_is_hook(blk)) { if(sparse_add_candidate(&blk->fingerprint, candidate)) { ret=CAND_RET_PERM; goto error; } } else if(sb->path.cmd==CMD_MANIFEST) { if(!(candidate=candidates_add_new())) { ret=CAND_RET_PERM; goto error; } candidate->path=sb->path.buf; sb->path.buf=NULL; } blk->fingerprint=0; } end: if(scores_grow(scores, candidates_len)) { ret=CAND_RET_PERM; goto error; } candidates_set_score_pointers(candidates, candidates_len, scores); scores_reset(scores); //logp("Now have %d candidates\n", (int)candidates_len); ret=CAND_RET_OK; error: fzp_close(&fzp); sbuf_free(&sb); blk_free(&blk); return ret; }
static int deal_with_data(struct asfd *asfd, struct sbuf *sb, BFILE *bfd, size_t *datalen, struct conf *conf) { int ret=-1; int forget=0; FILE *fp=NULL; size_t elen=0; char *extrameta=NULL; unsigned long long bytes=0; sb->compression=conf->compression; iobuf_copy(&sb->path, asfd->rbuf); iobuf_init(asfd->rbuf); #ifdef HAVE_WIN32 if(win32_lstat(sb->path.buf, &sb->statp, &sb->winattr)) #else if(lstat(sb->path.buf, &sb->statp)) #endif { logw(asfd, conf, "Path has vanished: %s", sb->path.buf); if(forget_file(asfd, sb, conf)) goto error; goto end; } if(size_checks(asfd, sb, conf)) forget++; if(!forget) { sb->compression=in_exclude_comp(conf->excom, sb->path.buf, conf->compression); if(attribs_encode(sb)) goto error; else if(open_file_for_sendl(asfd, #ifdef HAVE_WIN32 bfd, NULL, #else NULL, &fp, #endif sb->path.buf, sb->winattr, datalen, conf->atime, conf)) forget++; } if(forget) { if(forget_file(asfd, sb, conf)) goto error; goto end; } if(sb->path.cmd==CMD_METADATA || sb->path.cmd==CMD_ENC_METADATA || sb->path.cmd==CMD_VSS || sb->path.cmd==CMD_ENC_VSS #ifdef HAVE_WIN32 || conf->strip_vss #endif ) { if(get_extrameta(asfd, #ifdef HAVE_WIN32 bfd, #endif sb->path.buf, &sb->statp, &extrameta, &elen, sb->winattr, conf, datalen)) { logw(asfd, conf, "Meta data error for %s", sb->path.buf); goto end; } if(extrameta) { #ifdef HAVE_WIN32 if(conf->strip_vss) { free(extrameta); extrameta=NULL; elen=0; } #endif } else { logw(asfd, conf, "No meta data after all: %s", sb->path.buf); goto end; } } if(sb->path.cmd==CMD_FILE && sb->burp1->datapth.buf) { unsigned long long sentbytes=0; // Need to do sig/delta stuff. if(asfd->write(asfd, &(sb->burp1->datapth)) || asfd->write(asfd, &sb->attr) || asfd->write(asfd, &sb->path) || load_signature_and_send_delta(asfd, bfd, fp, &bytes, &sentbytes, conf, *datalen)) { logp("error in sig/delta for %s (%s)\n", sb->path.buf, sb->burp1->datapth.buf); goto end; } else { cntr_add(conf->cntr, CMD_FILE_CHANGED, 1); cntr_add_bytes(conf->cntr, bytes); cntr_add_sentbytes(conf->cntr, sentbytes); } } else { //logp("need to send whole file: %s\n", sb.path); // send the whole file. if((asfd->write(asfd, &sb->attr) || asfd->write(asfd, &sb->path)) || send_whole_file_w(asfd, sb, NULL, 0, &bytes, conf->encryption_password, conf, sb->compression, bfd, fp, extrameta, elen, *datalen)) goto end; else { cntr_add(conf->cntr, sb->path.cmd, 1); cntr_add_bytes(conf->cntr, bytes); cntr_add_sentbytes(conf->cntr, bytes); } } end: ret=0; error: #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. #else close_file_for_sendl(NULL, &fp, asfd); #endif sbuf_free_content(sb); if(extrameta) free(extrameta); return ret; }
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; }
// FIX THIS: Maybe should be in bfile.c. enum ofr_e open_for_restore(struct asfd *asfd, BFILE *bfd, const char *path, struct sbuf *sb, int vss_restore, struct cntr *cntr, enum protocol protocol) { static int flags; if(bfd->mode!=BF_CLOSED) { #ifdef HAVE_WIN32 if(bfd->path && !strcmp(bfd->path, path)) { // Already open after restoring the VSS data. // Time now for the actual file data. return OFR_OK; } else { #endif if(bfd->close(bfd, asfd)) { logp("error closing %s in %s()\n", path, __func__); return OFR_ERROR; } #ifdef HAVE_WIN32 } #endif } #ifdef HAVE_WIN32 // Some massive hacks to work around times that winattr was not // getting set correctly inside server side backups. // The EFS one will stop burp segfaulting when restoring affected // EFS files. if(sb->path.cmd==CMD_EFS_FILE) sb->winattr |= FILE_ATTRIBUTE_ENCRYPTED; if(S_ISDIR(sb->statp.st_mode)) sb->winattr |= FILE_ATTRIBUTE_DIRECTORY; #endif bfile_init(bfd, sb->winattr, cntr); #ifdef HAVE_WIN32 bfd->set_win32_api(bfd, vss_restore); #endif if(S_ISDIR(sb->statp.st_mode)) { // Windows directories are treated as having file data. flags=O_WRONLY|O_BINARY; mkdir(path, 0777); } else flags=O_WRONLY|O_BINARY|O_CREAT|O_TRUNC; if(bfd->open(bfd, asfd, path, flags, S_IRUSR | S_IWUSR)) { berrno be; berrno_init(&be); char msg[256]=""; snprintf(msg, sizeof(msg), "Could not open for writing %s: %s", path, berrno_bstrerror(&be, errno)); if(restore_interrupt(asfd, sb, msg, cntr, protocol)) return OFR_ERROR; return OFR_CONTINUE; } // Add attributes to bfd so that they can be set when it is closed. bfd->winattr=sb->winattr; memcpy(&bfd->statp, &sb->statp, sizeof(struct stat)); return OFR_OK; }
// Combine the phase1 and phase2 files into a new manifest. int backup_phase3_server(const char *phase2data, const char *unchangeddata, const char *manifest, int recovery, int compress, const char *client, struct cntr *p1cntr, struct cntr *cntr, struct config *cconf) { int ars=0; int ret=0; int pcmp=0; FILE *ucfp=NULL; FILE *p2fp=NULL; FILE *mp=NULL; gzFile mzp=NULL; struct sbuf ucb; struct sbuf p2b; char *manifesttmp=NULL; logp("Begin phase3 (merge manifests)\n"); if(!(manifesttmp=get_tmp_filename(manifest))) return -1; if(!(ucfp=open_file(unchangeddata, "rb")) || !(p2fp=open_file(phase2data, "rb")) || (compress && !(mzp=gzopen_file(manifesttmp, comp_level(cconf)))) || (!compress && !(mp=open_file(manifesttmp, "wb")))) { close_fp(&ucfp); gzclose_fp(&mzp); close_fp(&p2fp); close_fp(&mp); free(manifesttmp); return -1; } init_sbuf(&ucb); init_sbuf(&p2b); while(ucfp || p2fp) { if(ucfp && !ucb.path && (ars=sbuf_fill(ucfp, NULL, &ucb, cntr))) { if(ars<0) { ret=-1; break; } // ars==1 means it ended ok. close_fp(&ucfp); } if(p2fp && !p2b.path && (ars=sbuf_fill(p2fp, NULL, &p2b, cntr))) { if(ars<0) { ret=-1; break; } // ars==1 means it ended ok. close_fp(&p2fp); // In recovery mode, only want to read to the last // entry in the phase 2 file. if(recovery) break; } if(ucb.path && !p2b.path) { write_status(client, STATUS_MERGING, ucb.path, p1cntr, cntr); if(sbuf_to_manifest(&ucb, mp, mzp)) { ret=-1; break; } free_sbuf(&ucb); } else if(!ucb.path && p2b.path) { write_status(client, STATUS_MERGING, p2b.path, p1cntr, cntr); if(sbuf_to_manifest(&p2b, mp, mzp)) { ret=-1; break; } free_sbuf(&p2b); } else if(!ucb.path && !p2b.path) { continue; } else if(!(pcmp=sbuf_pathcmp(&ucb, &p2b))) { // They were the same - write one and free both. write_status(client, STATUS_MERGING, p2b.path, p1cntr, cntr); if(sbuf_to_manifest(&p2b, mp, mzp)) { ret=-1; break; } free_sbuf(&p2b); free_sbuf(&ucb); } else if(pcmp<0) { write_status(client, STATUS_MERGING, ucb.path, p1cntr, cntr); if(sbuf_to_manifest(&ucb, mp, mzp)) { ret=-1; break; } free_sbuf(&ucb); } else { write_status(client, STATUS_MERGING, p2b.path, p1cntr, cntr); if(sbuf_to_manifest(&p2b, mp, mzp)) { ret=-1; break; } free_sbuf(&p2b); } } free_sbuf(&ucb); free_sbuf(&p2b); close_fp(&p2fp); close_fp(&ucfp); if(close_fp(&mp)) { logp("error closing %s in backup_phase3_server\n", manifesttmp); ret=-1; } if(gzclose_fp(&mzp)) { logp("error gzclosing %s in backup_phase3_server\n", manifesttmp); ret=-1; } if(!ret) { if(do_rename(manifesttmp, manifest)) ret=-1; else { unlink(phase2data); unlink(unchangeddata); } } free(manifesttmp); logp("End phase3 (merge manifests)\n"); return ret; }
int set_extrameta(const char *path, char cmd, struct stat *statp, const char *extrameta, size_t metalen, struct cntr *cntr) { size_t l=0; char cmdtmp='\0'; unsigned int s=0; const char *metadata=NULL; int errors=0; metadata=extrameta; l=metalen; while(l>0) { char *m=NULL; if((sscanf(metadata, "%c%08X", &cmdtmp, &s))!=2) { logw(cntr, "sscanf of metadata failed for %s: %s\n", path, metadata); return -1; } metadata+=9; l-=9; if(!(m=(char *)malloc(s+1))) { logp("out of memory\n"); return -1; } memcpy(m, metadata, s); m[s]='\0'; metadata+=s; l-=s; switch(cmdtmp) { #if defined(HAVE_LINUX_OS) || \ defined(HAVE_FREEBSD_OS) || \ defined(HAVE_OPENBSD_OS) || \ defined(HAVE_NETBSD_OS) #ifdef HAVE_ACL case META_ACCESS_ACL: if(set_acl(path, statp, m, s, cmdtmp, cntr)) errors++; break; case META_DEFAULT_ACL: if(set_acl(path, statp, m, s, cmdtmp, cntr)) errors++; break; #endif #endif #if defined(HAVE_LINUX_OS) #ifdef HAVE_XATTR case META_XATTR: if(set_xattr(path, statp, m, s, cmdtmp, cntr)) errors++; break; #endif #endif #if defined(HAVE_FREEBSD_OS) || \ defined(HAVE_OPENBSD_OS) || \ defined(HAVE_NETBSD_OS) #ifdef HAVE_XATTR case META_XATTR_BSD: if(set_xattr(path, statp, m, s, cmdtmp, cntr)) errors++; break; #endif #endif default: logp("unknown metadata: %c\n", cmdtmp); logw(cntr, "unknown metadata: %c\n", cmdtmp); errors++; break; } free(m); } return errors; }
int authorise_client(struct asfd *asfd, struct conf *conf, char **server_version) { int ret=-1; char hello[256]=""; struct iobuf *rbuf=asfd->rbuf; snprintf(hello, sizeof(hello), "hello:%s", VERSION); if(asfd->write_str(asfd, CMD_GEN, hello)) { logp("problem with auth\n"); goto end; } if(asfd->read(asfd) || rbuf->cmd!=CMD_GEN || strncmp_w(rbuf->buf, "whoareyou")) { logp("problem with auth\n"); goto end; } if(rbuf->buf) { char *cp=NULL; if((cp=strchr(rbuf->buf, ':'))) { cp++; if(cp) { if(!(*server_version=strdup(cp))) { log_out_of_memory(__func__); goto end; } } } iobuf_free_content(rbuf); } if(asfd->write_str(asfd, CMD_GEN, conf->cname) || asfd->read_expect(asfd, CMD_GEN, "okpassword") || asfd->write_str(asfd, CMD_GEN, conf->password) || asfd->read(asfd)) { logp("problem with auth\n"); goto end; } if(rbuf->cmd==CMD_WARNING) // special case for the version warning { //logw(conf->p1cntr, rbuf->buf); logp("WARNING: %s\n", rbuf->buf); cntr_add(conf->cntr, rbuf->cmd, 0); iobuf_free_content(rbuf); if(asfd->read(asfd)) { logp("problem with auth\n"); goto end; } } if(rbuf->cmd==CMD_GEN && !strcmp(rbuf->buf, "ok")) { // It is OK. logp("auth ok\n"); } else { iobuf_log_unexpected(rbuf, __func__); goto end; } ret=0; end: iobuf_free_content(rbuf); return ret; }
int backup_phase1_server_all(struct async *as, struct sdirs *sdirs, struct conf *conf) { int ars=0; int ret=-1; struct sbuf *sb=NULL; gzFile p1zp=NULL; char *phase1tmp=NULL; struct asfd *asfd=as->asfd; logp("Begin phase1 (file system scan)\n"); if(!(phase1tmp=get_tmp_filename(sdirs->phase1data))) goto end; if(!(p1zp=gzopen_file(phase1tmp, comp_level(conf)))) goto end; if(!(sb=sbuf_alloc(conf))) goto end; while(1) { sbuf_free_content(sb); if(conf->protocol==PROTO_BURP1) ars=sbufl_fill(sb, asfd, NULL, NULL, conf->cntr); else ars=sbuf_fill(sb, asfd, NULL, NULL, NULL, conf); if(ars) { if(ars<0) goto end; //ars==1 means it ended ok. // Last thing the client sends is 'backupphase2', and // it wants an 'ok' reply. if(asfd->write_str(asfd, CMD_GEN, "ok") || send_msg_zp(p1zp, CMD_GEN, "phase1end", strlen("phase1end"))) goto end; break; } if(write_status(STATUS_SCANNING, sb->path.buf, conf) || sbufl_to_manifest_phase1(sb, NULL, p1zp)) goto end; cntr_add_phase1(conf->cntr, sb->path.cmd, 0); if(sb->path.cmd==CMD_FILE || sb->path.cmd==CMD_ENC_FILE || sb->path.cmd==CMD_METADATA || sb->path.cmd==CMD_ENC_METADATA || sb->path.cmd==CMD_EFS_FILE) cntr_add_val(conf->cntr, CMD_BYTES_ESTIMATED, (unsigned long long)sb->statp.st_size, 0); } if(gzclose_fp(&p1zp)) { logp("error closing %s in backup_phase1_server\n", phase1tmp); goto end; } // 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 end; //cntr_print(p1cntr, cntr, ACTION_BACKUP); logp("End phase1 (file system scan)\n"); ret=0; end: free(phase1tmp); gzclose_fp(&p1zp); sbuf_free(&sb); 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; }
/* * If the stream has no more data available, read some from F into * BUF, and let the stream use that. On return, SEEN_EOF is true if * the end of file has passed into the stream. */ rs_result rs_infilebuf_fill(rs_job_t *job, rs_buffers_t *buf, void *opaque) { int len=0; rs_filebuf_t *fb=(rs_filebuf_t *) opaque; struct cntr *cntr; int fd=fb->fd; cntr=fb->cntr; //logp("rs_infilebuf_fill\n"); /* This is only allowed if either the buf has no input buffer * yet, or that buffer could possibly be BUF. */ if(buf->next_in) { //logp("infilebuf avail_in %d buf_len %d\n", // buf->avail_in, fb->buf_len); if(buf->avail_in > fb->buf_len) { logp("buf->avail_in > fb->buf_len (%d > %d) in %s\n", buf->avail_in, fb->buf_len, __func__); return RS_IO_ERROR; } if(buf->next_in < fb->buf) { logp("buf->next_in < fb->buf in %s\n", __func__); return RS_IO_ERROR; } if(buf->next_in > fb->buf + fb->buf_len) { logp("buf->next_in > fb->buf + fb->buf_len in %s\n", __func__); return RS_IO_ERROR; } } else { if(buf->avail_in) { logp("buf->avail_in is %d in %s\n", buf->avail_in, __func__); return RS_IO_ERROR; } } if(buf->eof_in) return RS_DONE; if(buf->avail_in) /* Still some data remaining. Perhaps we should read anyhow? */ return RS_DONE; if(fd>=0) { static struct iobuf *rbuf=NULL; rbuf=fb->asfd->rbuf; if(fb->asfd->read(fb->asfd)) return RS_IO_ERROR; if(rbuf->cmd==CMD_APPEND) { //logp("got '%c' in fd infilebuf: %d\n", // CMD_APPEND, rbuf->len); memcpy(fb->buf, rbuf->buf, rbuf->len); len=rbuf->len; iobuf_free_content(rbuf); } else if(rbuf->cmd==CMD_END_FILE) { iobuf_free_content(rbuf); //logp("got %c in fd infilebuf\n", CMD_END_FILE); buf->eof_in=1; return RS_DONE; } else if(rbuf->cmd==CMD_WARNING) { logp("WARNING: %s\n", rbuf->buf); cntr_add(cntr, rbuf->cmd, 0); iobuf_free_content(rbuf); return RS_DONE; } else { iobuf_log_unexpected(rbuf, __func__); iobuf_free_content(rbuf); return RS_IO_ERROR; } } else if(fb->bfd) { if(fb->do_known_byte_count) { if(fb->data_len>0) { len=fb->bfd->read(fb->bfd, fb->buf, min(fb->buf_len, fb->data_len)); fb->data_len-=len; } else { // We have already read as much data as the VSS // header told us to, so set len=0 in order to // finish up. len=0; } } else len=fb->bfd->read(fb->bfd, fb->buf, fb->buf_len); if(len==0) { //logp("bread: eof\n"); buf->eof_in=1; return RS_DONE; } else if(len<0) { logp("rs_infilebuf_fill: error in bread\n"); return RS_IO_ERROR; } //logp("bread: ok: %d\n", len); fb->bytes+=len; if(!MD5_Update(&(fb->md5), fb->buf, len)) { logp("rs_infilebuf_fill: MD5_Update() failed\n"); return RS_IO_ERROR; } } else if(fb->fzp) { if((len=fzp_read(fb->fzp, fb->buf, fb->buf_len))<=0) { // This will happen if file size is a multiple of // input block len. if(fzp_eof(fb->fzp)) { buf->eof_in=1; return RS_DONE; } else { logp("rs_infilebuf_fill: got return %d when trying to read\n", len); return RS_IO_ERROR; } } fb->bytes+=len; if(!MD5_Update(&(fb->md5), fb->buf, len)) { logp("rs_infilebuf_fill: MD5_Update() failed\n"); return RS_IO_ERROR; } } buf->avail_in = len; buf->next_in = fb->buf; return RS_DONE; }
static int load_signature_and_send_delta(BFILE *bfd, FILE *in, unsigned long long *bytes, unsigned long long *sentbytes, struct cntr *cntr) { rs_job_t *job; rs_result r; rs_signature_t *sumset=NULL; unsigned char checksum[MD5_DIGEST_LENGTH+1]; rs_filebuf_t *infb=NULL; rs_filebuf_t *outfb=NULL; rs_buffers_t rsbuf; memset(&rsbuf, 0, sizeof(rsbuf)); if(load_signature(&sumset, cntr)) return -1; //logp("start delta\n"); if(!(job=rs_delta_begin(sumset))) { logp("could not start delta job.\n"); rs_free_sumset(sumset); return RS_IO_ERROR; } if(!(infb=rs_filebuf_new(bfd, in, NULL, -1, ASYNC_BUF_LEN, cntr)) || !(outfb=rs_filebuf_new(NULL, NULL, NULL, async_get_fd(), ASYNC_BUF_LEN, cntr))) { logp("could not rs_filebuf_new for delta\n"); if(infb) rs_filebuf_free(infb); return -1; } //logp("start delta loop\n"); while(1) { size_t wlen=0; rs_result delresult; delresult=rs_async(job, &rsbuf, infb, outfb); if(delresult==RS_DONE) { r=delresult; // logp("delresult done\n"); break; } else if(delresult==RS_BLOCKED || delresult==RS_RUNNING) { // logp("delresult running/blocked: %d\n", delresult); // Keep going } else { logp("error in rs_async for delta: %d\n", delresult); r=delresult; break; } // FIX ME: get it to read stuff (errors, for example) here too. if(async_rw(NULL, NULL, '\0', '\0', NULL, &wlen)) return -1; } if(r!=RS_DONE) logp("delta loop returned: %d\n", r); //logp("after delta loop: %d\n", r); //logp("\n"); if(r==RS_DONE) { *bytes=infb->bytes; *sentbytes=outfb->bytes; if(!MD5_Final(checksum, &(infb->md5))) { logp("MD5_Final() failed\n"); r=RS_IO_ERROR; } } rs_filebuf_free(infb); rs_filebuf_free(outfb); rs_job_free(job); rs_free_sumset(sumset); if(r==RS_DONE && write_endfile(*bytes, checksum)) // finish delta file return -1; //logp("end of load_sig_send_delta\n"); return r; }
int do_restore_client_burp1(struct asfd *asfd, struct conf *conf, enum action act, int vss_restore) { int ars=0; int ret=-1; char msg[512]=""; struct sbuf *sb=NULL; // Windows needs to have the VSS data written first, and the actual data // written immediately afterwards. The server is transferring them in two // chunks. So, leave bfd open after a Windows metadata transfer. BFILE bfd; #ifdef HAVE_WIN32 binit(&bfd, 0, conf); #endif logp("doing %s\n", act_str(act)); snprintf(msg, sizeof(msg), "%s %s:%s", act_str(act), conf->backup?conf->backup:"", conf->regex?conf->regex:""); if(asfd->write_str(asfd, CMD_GEN, msg) || asfd->read_expect(asfd, CMD_GEN, "ok")) return -1; logp("doing %s confirmed\n", act_str(act)); if(conf->send_client_cntr) { // FIX THIS // if(cntr_recv(conf)) goto end; } #if defined(HAVE_WIN32) if(act==ACTION_RESTORE) win32_enable_backup_privileges(); #endif if(!(sb=sbuf_alloc(conf))) goto end; while(1) { char *fullpath=NULL; sbuf_free_content(sb); if((ars=sbufl_fill(sb, asfd, NULL, NULL, conf->cntr))) { if(ars<0) goto end; else { // ars==1 means it ended ok. //logp("got %s end\n", act_str(act)); if(asfd->write_str(asfd, CMD_GEN, "restoreend ok")) goto end; } break; } 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(conf->strip) { int s; s=strip_path_components(asfd, sb, &(sb->path.buf), conf); if(s<0) goto end; // error else if(s==0) { // Too many components stripped // - carry on. continue; } // It is OK, sb->path is now stripped. } if(!(fullpath=prepend_s(conf->restoreprefix, sb->path.buf))) { log_and_send_oom(asfd, __func__); goto end; } if(act==ACTION_RESTORE) { strip_invalid_characters(&fullpath); if(!overwrite_ok(sb, conf, &bfd, fullpath)) { char msg[512]=""; // Something exists at that path. snprintf(msg, sizeof(msg), "Path exists: %s", fullpath); if(restore_interrupt(asfd, sb, msg, conf)) goto end; else { if(fullpath) free(fullpath); continue; } } } break; default: break; } switch(sb->path.cmd) { case CMD_WARNING: cntr_add(conf->cntr, sb->path.cmd, 1); printf("\n"); logp("%s", sb->path); break; case CMD_DIRECTORY: if(restore_dir(asfd, sb, fullpath, act, conf)) goto end; break; case CMD_FILE: case CMD_VSS_T: // Have it a separate statement to the // encrypted version so that encrypted and not // encrypted files can be restored at the // same time. if(restore_file_or_get_meta(asfd, &bfd, sb, fullpath, act, NULL, NULL, NULL, vss_restore, conf)) { logp("restore_file error\n"); goto end; } break; case CMD_ENC_FILE: case CMD_ENC_VSS_T: if(restore_file_or_get_meta(asfd, &bfd, sb, fullpath, act, conf->encryption_password, NULL, NULL, vss_restore, conf)) { logp("restore_file error\n"); goto end; } break; case CMD_SOFT_LINK: case CMD_HARD_LINK: if(restore_link(asfd, sb, fullpath, conf->restoreprefix, act, conf)) goto end; break; case CMD_SPECIAL: if(restore_special(asfd, sb, fullpath, act, conf)) goto end; break; case CMD_METADATA: case CMD_VSS: if(restore_metadata(asfd, &bfd, sb, fullpath, act, NULL, vss_restore, conf)) goto end; break; case CMD_ENC_METADATA: case CMD_ENC_VSS: if(restore_metadata(asfd, &bfd, sb, fullpath, act, conf->encryption_password, vss_restore, conf)) goto end; break; case CMD_EFS_FILE: if(restore_file_or_get_meta(asfd, &bfd, sb, fullpath, act, NULL, NULL, NULL, vss_restore, conf)) { logp("restore_file error\n"); goto end; } break; default: logp("unknown cmd: %c\n", sb->path.cmd); goto end; break; } if(fullpath) free(fullpath); } ret=0; end: sbuf_free(sb); #ifdef HAVE_WIN32 // It is possible for a bfd to still be open. bclose(&bfd, asfd); #endif cntr_print_end(conf->cntr); cntr_print(conf->cntr, act); if(!ret) logp("%s finished\n", act_str(act)); else logp("ret: %d\n", ret); return ret; }
// Return p1manio position. static man_off_t *do_resume_work(struct sdirs *sdirs, struct dpth *dpth, struct conf **cconfs) { man_off_t *pos=NULL; man_off_t *p1pos=NULL; struct iobuf *chb=NULL; struct manio *cmanio=NULL; struct manio *umanio=NULL; struct manio *p1manio=NULL; enum protocol protocol=get_protocol(cconfs); struct cntr *cntr=get_cntr(cconfs); int compression=get_int(cconfs[OPT_COMPRESSION]); if(!(p1manio=manio_open_phase1(sdirs->phase1data, "rb", protocol)) || !(cmanio=manio_open_phase2(sdirs->changed, "rb", protocol)) || !(umanio=manio_open_phase2(sdirs->unchanged, "rb", protocol))) goto end; if(!(chb=iobuf_alloc())) return NULL; logp("Setting up resume positions...\n"); if(get_last_good_entry(cmanio, chb, cntr, dpth, protocol, &pos)) goto error; if(manio_close_and_truncate(&cmanio, pos, compression)) goto error; man_off_t_free(&pos); if(chb->buf) { logp(" last good entry: %s\n", chb->buf); // Now need to go to the appropriate places in p1manio and // unchanged. if(forward_past_entry(p1manio, chb, protocol, &p1pos)) goto error; // The unchanged file needs to be positioned just before the // found entry, otherwise it ends up having a duplicated entry. if(forward_before_entry(umanio, chb, cntr, dpth, protocol, &pos)) goto error; if(manio_close_and_truncate(&umanio, pos, compression)) goto error; man_off_t_free(&pos); } else { logp(" nothing previously transferred\n"); if(!(p1pos=manio_tell(p1manio))) goto error; if(!(pos=manio_tell(umanio))) goto error; if(manio_close_and_truncate(&umanio, pos, compression)) goto error; } // Now should have all file pointers in the right places to resume. goto end; error: man_off_t_free(&p1pos); end: iobuf_free(&chb); man_off_t_free(&pos); manio_close(&p1manio); manio_close(&cmanio); manio_close(&umanio); return p1pos; }
// The failure conditions here are dealt with by the rubble cleaning code. static int delete_backup(struct sdirs *sdirs, const char *cname, struct bu *bu, const char *manual_delete) { logp("deleting %s backup %"PRId64"\n", cname, bu->bno); if(sdirs->global_sparse) { const char *candidate_str=bu->path+strlen(sdirs->base)+1; if(remove_from_global_sparse( sdirs->global_sparse, candidate_str)) return -1; } if(!bu->next && !bu->prev) { // The current, and only, backup. if(do_rename_w(bu->path, sdirs->deleteme, cname, bu)) return -1; // If interrupted here, there will be a dangling 'current' // symlink. if(unlink(sdirs->current)) { logp("unlink %s: %s\n", sdirs->current, strerror(errno)); return -1; } return recursive_delete_w(sdirs, bu, manual_delete); } if(!bu->next && bu->prev) { // The current backup. There are other backups left. // Need to point the symlink at the previous backup. const char *target=NULL; target=bu->prev->basename; unlink(sdirs->currenttmp); if(symlink(target, sdirs->currenttmp)) { logp("could not symlink '%s' to '%s': %s\n", sdirs->currenttmp, target, strerror(errno)); return -1; } // If interrupted here, there is a currenttmp and a current // symlink, and they both point to valid directories. if(do_rename_w(bu->path, sdirs->deleteme, cname, bu)) return -1; // If interrupted here, there is a currenttmp and a current // symlink, and the current link is dangling. if(do_rename(sdirs->currenttmp, sdirs->current)) return -1; // If interrupted here, moving the symlink could have failed // after current was deleted but before currenttmp was renamed. if(recursive_delete_w(sdirs, bu, manual_delete)) return -1; return 0; } // It is not the current backup. if(do_rename_w(bu->path, sdirs->deleteme, cname, bu) || recursive_delete_w(sdirs, bu, manual_delete)) return -1; return 0; }
int restore_switch_burp1(struct asfd *asfd, struct sbuf *sb, const char *fullpath, enum action act, BFILE *bfd, int vss_restore, struct conf *conf) { switch(sb->path.cmd) { case CMD_FILE: case CMD_VSS_T: // Have it a separate statement to the // encrypted version so that encrypted and not // encrypted files can be restored at the // same time. if(restore_file_or_get_meta(asfd, bfd, sb, fullpath, act, NULL, NULL, NULL, vss_restore, conf)) { logp("restore_file error\n"); goto error; } break; case CMD_ENC_FILE: case CMD_ENC_VSS_T: if(restore_file_or_get_meta(asfd, bfd, sb, fullpath, act, conf->encryption_password, NULL, NULL, vss_restore, conf)) { logp("restore_file error\n"); goto error; } break; case CMD_METADATA: case CMD_VSS: if(restore_metadata(asfd, bfd, sb, fullpath, act, NULL, vss_restore, conf)) goto error; break; case CMD_ENC_METADATA: case CMD_ENC_VSS: if(restore_metadata(asfd, bfd, sb, fullpath, act, conf->encryption_password, vss_restore, conf)) goto error; break; case CMD_EFS_FILE: if(restore_file_or_get_meta(asfd, bfd, sb, fullpath, act, NULL, NULL, NULL, vss_restore, conf)) { logp("restore_file error\n"); goto error; } break; default: // Other cases (dir/links/etc) are handled in the // calling function. logp("unknown cmd: %c\n", sb->path.cmd); goto error; } return 0; error: return -1; }
int backup_phase2_client_protocol2(struct asfd *asfd, struct conf **confs, int resume) { int ret=-1; uint8_t end_flags=0; struct slist *slist=NULL; struct iobuf *rbuf=NULL; struct iobuf *wbuf=NULL; struct cntr *cntr=NULL; if(confs) cntr=get_cntr(confs); if(!asfd || !asfd->as) { logp("%s() called without async structs!\n", __func__); goto end; } logp("Phase 2 begin (send backup data)\n"); logfmt("\n"); if(!(slist=slist_alloc()) || !(wbuf=iobuf_alloc()) || blks_generate_init()) goto end; rbuf=asfd->rbuf; if(!resume) { // Only do this bit if the server did not tell us to resume. if(asfd->write_str(asfd, CMD_GEN, "backupphase2") || asfd_read_expect(asfd, CMD_GEN, "ok")) goto end; } else { // On resume, the server might update the client with cntr. if(cntr_recv(asfd, confs)) goto end; } while(!(end_flags&END_BACKUP)) { if(!wbuf->len) { get_wbuf_from_data(confs, wbuf, slist, end_flags); if(!wbuf->len) { if(get_wbuf_from_blks(wbuf, slist, &end_flags)) goto end; } } if(wbuf->len) { if(asfd->append_all_to_write_buffer(asfd, wbuf) ==APPEND_ERROR) goto end; } if(asfd->as->read_write(asfd->as)) { logp("error in %s\n", __func__); goto end; } if(rbuf->buf && deal_with_read(rbuf, slist, cntr, &end_flags)) goto end; if(slist->head // Need to limit how many blocks are allocated at once. && (!slist->blist->head || slist->blist->tail->index - slist->blist->head->index<BLKS_MAX_IN_MEM) ) { if(add_to_blks_list(asfd, confs, slist)) goto end; } if(end_flags&END_BLK_REQUESTS) { // If got to the end of the file request list // and the last block of the last file, and // the write buffer is empty, we got to the end. if(slist->head==slist->tail) { if(!slist->tail || slist->blist->last_sent== slist->tail->protocol2->bend) { if(!wbuf->len) break; } } } } if(asfd->write_str(asfd, CMD_GEN, "backup_end")) goto end; ret=0; end: slist_free(&slist); blks_generate_free(); if(wbuf) { // Write buffer did not allocate 'buf'. wbuf->buf=NULL; iobuf_free(&wbuf); } cntr_print_end(cntr); cntr_print(cntr, ACTION_BACKUP, asfd); if(ret) logp("Error in backup\n"); logp("End backup\n"); return ret; }
static int deal_with_read(struct iobuf *rbuf, struct slist *slist, struct cntr *cntr, uint8_t *end_flags) { int ret=0; switch(rbuf->cmd) { /* Incoming file request. */ case CMD_FILE: case CMD_METADATA: if(add_to_file_requests(slist, rbuf)) goto error; return 0; /* Incoming data block request. */ case CMD_DATA_REQ: if(add_to_data_requests(slist->blist, rbuf)) goto error; goto end; /* Incoming control/message stuff. */ case CMD_WRAP_UP: { int64_t wrap_up; struct blk *blk; struct blist *blist=slist->blist; from_base64(&wrap_up, rbuf->buf); for(blk=blist->head; blk; blk=blk->next) { if(blk->index==(uint64_t)wrap_up) { blist->last_requested=blk; blist->last_sent=blk; break; } } if(!blk) { logp("Could not find wrap up index: %016" PRIX64 "\n", wrap_up); // goto error; } goto end; } case CMD_MESSAGE: case CMD_WARNING: { log_recvd(rbuf, cntr, 0); goto end; } case CMD_GEN: if(!strcmp(rbuf->buf, "requests_end")) { (*end_flags)|=END_REQUESTS; goto end; } else if(!strcmp(rbuf->buf, "blk_requests_end")) { (*end_flags)|=END_BLK_REQUESTS; goto end; } else if(!strcmp(rbuf->buf, "backup_end")) { (*end_flags)|=END_BACKUP; goto end; } break; default: break; } iobuf_log_unexpected(rbuf, __func__); error: ret=-1; end: iobuf_free_content(rbuf); return ret; }
int run_bedup(int argc, char *argv[]) { int i=1; int ret=0; int option=0; int nonburp=0; unsigned int maxlinks=DEF_MAX_LINKS; char *groups=NULL; char ext[16]=""; int givenconfigfile=0; const char *configfile=NULL; configfile=get_config_path(); snprintf(ext, sizeof(ext), ".bedup.%d", getpid()); while((option=getopt(argc, argv, "c:dg:hlm:nvV?"))!=-1) { switch(option) { case 'c': configfile=optarg; givenconfigfile=1; break; case 'd': deletedups=1; break; case 'g': groups=optarg; break; case 'l': makelinks=1; break; case 'm': maxlinks=atoi(optarg); break; case 'n': nonburp=1; break; case 'V': printf("%s-%s\n", prog, VERSION); return 0; case 'v': verbose=1; break; case 'h': case '?': return usage(); } } if(nonburp && givenconfigfile) { logp("-n and -c options are mutually exclusive\n"); return 1; } if(nonburp && groups) { logp("-n and -g options are mutually exclusive\n"); return 1; } if(!nonburp && maxlinks!=DEF_MAX_LINKS) { logp("-m option is specified via the configuration file in burp mode (max_hardlinks=)\n"); return 1; } if(deletedups && makelinks) { logp("-d and -l options are mutually exclusive\n"); return 1; } if(deletedups && !nonburp) { logp("-d option requires -n option\n"); return 1; } if(optind>=argc) { if(nonburp) { logp("No directories found after options\n"); return 1; } } else { if(!nonburp) { logp("Do not specify extra arguments.\n"); return 1; } } if(maxlinks<2) { logp("The argument to -m needs to be greater than 1.\n"); return 1; } if(nonburp) { // Read directories from command line. for(i=optind; i<argc; i++) { // Strip trailing slashes, for tidiness. if(argv[i][strlen(argv[i])-1]=='/') argv[i][strlen(argv[i])-1]='\0'; if(process_dir("", argv[i], ext, maxlinks, 0 /* not burp mode */, 0 /* level */)) { ret=1; break; } } } else { struct conf **globalcs=NULL; struct strlist *grouplist=NULL; struct lock *globallock=NULL; if(groups) { char *tok=NULL; if((tok=strtok(groups, ",\n"))) { do { if(strlist_add(&grouplist, tok, 1)) { log_out_of_memory(__func__); return -1; } } while((tok=strtok(NULL, ",\n"))); } if(!grouplist) { logp("unable to read list of groups\n"); return -1; } } // Read directories from config files, and get locks. if(!(globalcs=confs_alloc())) return -1; if(confs_init(globalcs)) return -1; if(conf_load_global_only(configfile, globalcs)) return 1; if(get_e_burp_mode(globalcs[OPT_BURP_MODE])!=BURP_MODE_SERVER) { logp("%s is not a server config file\n", configfile); confs_free(&globalcs); return 1; } logp("Dedup clients from %s\n", get_string(globalcs[OPT_CLIENTCONFDIR])); maxlinks=get_int(globalcs[OPT_MAX_HARDLINKS]); if(grouplist) { struct strlist *g=NULL; logp("in dedup groups:\n"); for(g=grouplist; g; g=g->next) logp("%s\n", g->path); } else { char *lockpath=NULL; const char *opt_lockfile=confs_get_lockfile(globalcs); // Only get the global lock when doing a global run. // If you are doing individual groups, you are likely // to want to do many different dedup jobs and a // global lock would get in the way. if(!(lockpath=prepend(opt_lockfile, ".bedup")) || !(globallock=lock_alloc_and_init(lockpath))) return 1; lock_get(globallock); if(globallock->status!=GET_LOCK_GOT) { logp("Could not get lock %s (%d)\n", lockpath, globallock->status); free_w(&lockpath); return 1; } logp("Got %s\n", lockpath); } ret=iterate_over_clients(globalcs, grouplist, ext, maxlinks); confs_free(&globalcs); lock_release(globallock); lock_free(&globallock); strlists_free(&grouplist); } if(!nonburp) { logp("%d client storages scanned\n", ccount); } logp("%llu duplicate %s found\n", count, count==1?"file":"files"); logp("%llu bytes %s%s\n", savedbytes, (makelinks || deletedups)?"saved":"saveable", bytes_to_human(savedbytes)); return ret; }
static int iterate_over_clients(struct conf **globalcs, struct strlist *grouplist, const char *ext, unsigned int maxlinks) { int ret=0; DIR *dirp=NULL; struct conf **cconfs=NULL; struct dirent *dirinfo=NULL; const char *globalclientconfdir=get_string(globalcs[OPT_CLIENTCONFDIR]); signal(SIGABRT, &sighandler); signal(SIGTERM, &sighandler); signal(SIGINT, &sighandler); if(!(cconfs=confs_alloc())) return -1; if(confs_init(cconfs)) return -1; if(!(dirp=opendir(globalclientconfdir))) { logp("Could not opendir '%s': %s\n", globalclientconfdir, strerror(errno)); return 0; } while((dirinfo=readdir(dirp))) { char *lockfile=NULL; char *lockfilebase=NULL; char *client_lockdir=NULL; struct lock *lock=NULL; if(dirinfo->d_ino==0 // looks_like...() also avoids '.' and '..'. || looks_like_tmp_or_hidden_file(dirinfo->d_name) || !is_regular_file(globalclientconfdir, dirinfo->d_name)) continue; confs_free_content(cconfs); if(confs_init(cconfs)) return -1; if(set_string(cconfs[OPT_CNAME], dirinfo->d_name)) return -1; if(conf_load_clientconfdir(globalcs, cconfs)) { logp("could not load config for client %s\n", dirinfo->d_name); return 0; } if(grouplist) { const char *dedup_group= get_string(cconfs[OPT_DEDUP_GROUP]); if(!dedup_group || !in_group(grouplist, dedup_group)) continue; } if(!(client_lockdir=get_string(cconfs[OPT_CLIENT_LOCKDIR]))) client_lockdir=get_string(cconfs[OPT_DIRECTORY]); if(!(lockfilebase=prepend_s(client_lockdir, dirinfo->d_name)) || !(lockfile=prepend_s(lockfilebase, BEDUP_LOCKFILE_NAME))) { free_w(&lockfilebase); free_w(&lockfile); ret=-1; break; } free_w(&lockfilebase); if(!(lock=lock_alloc_and_init(lockfile))) { ret=-1; break; } lock_get(lock); free_w(&lockfile); if(lock->status!=GET_LOCK_GOT) { logp("Could not get %s\n", lock->path); continue; } logp("Got %s\n", lock->path); // Remember that we got that lock. lock_add_to_list(&locklist, lock); switch(process_dir(get_string(cconfs[OPT_DIRECTORY]), dirinfo->d_name, ext, maxlinks, 1 /* burp mode */, 0 /* level */)) { case 0: ccount++; case 1: continue; default: ret=-1; break; } break; } closedir(dirp); locks_release_and_free(&locklist); confs_free(&cconfs); return ret; }
// Return 0 for directory processed, -1 for error, 1 for not processed. static int process_dir(const char *oldpath, const char *newpath, const char *ext, unsigned int maxlinks, int burp_mode, int level) { int ret=-1; DIR *dirp=NULL; char *path=NULL; struct stat info; struct dirent *dirinfo=NULL; struct file newfile; struct mystruct *find=NULL; static char working[256]=""; static char finishing[256]=""; newfile.path=NULL; if(!(path=prepend_s(oldpath, newpath))) goto end; if(burp_mode && level==0) { if(get_link(path, "working", working, sizeof(working)) || get_link(path, "finishing", finishing, sizeof(finishing))) goto end; if(!looks_like_protocol1(path)) { logp("%s does not look like a protocol 1 storage directory - skipping\n", path); ret=1; goto end; } } if(!(dirp=opendir(path))) { logp("Could not opendir '%s': %s\n", path, strerror(errno)); ret=1; goto end; } while((dirinfo=readdir(dirp))) { if(!strcmp(dirinfo->d_name, ".") || !strcmp(dirinfo->d_name, "..")) continue; //printf("try %s\n", dirinfo->d_name); if(burp_mode && level_exclusion(level, dirinfo->d_name, working, finishing)) continue; free_w(&newfile.path); if(!(newfile.path=prepend_s(path, dirinfo->d_name))) goto end; if(lstat(newfile.path, &info)) continue; if(S_ISDIR(info.st_mode)) { if(process_dir(path, dirinfo->d_name, ext, maxlinks, burp_mode, level+1)) goto end; continue; } else if(!S_ISREG(info.st_mode) || !info.st_size) // ignore zero-length files continue; newfile.dev=info.st_dev; newfile.ino=info.st_ino; newfile.nlink=info.st_nlink; newfile.full_cksum=0; newfile.part_cksum=0; newfile.next=NULL; if((find=find_key(info.st_size))) { //printf("check %d: %s\n", info.st_size, newfile.path); if(check_files(find, &newfile, &info, ext, maxlinks)) goto end; } else { //printf("add: %s\n", newfile.path); if(add_key(info.st_size, &newfile)) goto end; } } ret=0; end: closedir(dirp); free_w(&newfile.path); free_w(&path); return ret; }
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; }
int recursive_hardlink(const char *src, const char *dst, const char *client, struct cntr *p1cntr, struct cntr *cntr, struct config *conf) { int n=-1; int ret=0; struct dirent **dir; char *tmp=NULL; //logp("in rec hl: %s %s\n", src, dst); if(!(tmp=prepend_s(dst, "dummy", strlen("dummy")))) return -1; if(mkpath(&tmp, dst)) { logp("could not mkpath for %s\n", tmp); free(tmp); return -1; } free(tmp); if((n=scandir(src, &dir, 0, 0))<0) { logp("recursive_hardlink scandir %s: %s\n", src, strerror(errno)); return -1; } while(n--) { struct stat statp; char *fullpatha=NULL; char *fullpathb=NULL; if(dir[n]->d_ino==0 || !strcmp(dir[n]->d_name, ".") || !strcmp(dir[n]->d_name, "..")) { free(dir[n]); continue; } if(!(fullpatha=prepend_s(src, dir[n]->d_name, strlen(dir[n]->d_name))) || !(fullpathb=prepend_s(dst, dir[n]->d_name, strlen(dir[n]->d_name)))) { if(fullpatha) free(fullpatha); if(fullpathb) free(fullpathb); break; } if(lstat(fullpatha, &statp)) { logp("could not lstat %s\n", fullpatha); } else if(S_ISDIR(statp.st_mode)) { if(recursive_hardlink(fullpatha, fullpathb, client, p1cntr, cntr, conf)) { free(fullpatha); free(fullpathb); break; } } else { //logp("hardlinking %s to %s\n", fullpathb, fullpatha); write_status(client, STATUS_SHUFFLING, fullpathb, p1cntr, cntr); if(do_link(fullpatha, fullpathb, &statp, conf, FALSE /* do not overwrite target */)) { free(fullpatha); free(fullpathb); break; } } free(fullpatha); free(fullpathb); free(dir[n]); } if(n>0) { ret=-1; for(; n>0; n--) free(dir[n]); } free(dir); return ret; }