static struct slist *do_build_manifest(struct manio *manio, enum protocol protocol, int entries, int with_data_files) { struct sbuf *sb; struct slist *slist=NULL; slist=build_slist(protocol, entries, with_data_files); for(sb=slist->head; sb; sb=sb->next) { fail_unless(!manio_write_sbuf(manio, sb)); if(protocol==PROTO_2) { struct blk *blk=NULL; for(blk=sb->protocol2->bstart; blk && blk!=sb->protocol2->bend; blk=blk->next) { fail_unless(!manio_write_sig_and_path(manio, blk)); } if(sbuf_is_filedata(sb) || sbuf_is_vssdata(sb)) { struct iobuf endfile; iobuf_from_str(&endfile, CMD_END_FILE, (char *)"0:0"); fail_unless(!iobuf_send_msg_fzp(&endfile, manio->fzp)); } hack_protocol2_attr(&sb->attr); } } return slist; }
int restore_sbuf_protocol1(struct asfd *asfd, struct sbuf *sb, struct bu *bu, enum action act, struct sdirs *sdirs, enum cntr_status cntr_status, struct conf **cconfs) { if((sb->protocol1->datapth.buf && asfd->write(asfd, &(sb->protocol1->datapth))) || asfd->write(asfd, &sb->attr)) return -1; else if(sbuf_is_filedata(sb) || sbuf_is_vssdata(sb)) { if(!sb->protocol1->datapth.buf) { logw(asfd, get_cntr(cconfs), "Got filedata entry with no datapth: %c:%s\n", sb->path.cmd, sb->path.buf); return 0; } return restore_file(asfd, bu, sb, act, sdirs, cconfs); } else { if(asfd->write(asfd, &sb->path)) return -1; // If it is a link, send what // it points to. else if(sbuf_is_link(sb) && asfd->write(asfd, &sb->link)) return -1; cntr_add(get_cntr(cconfs), sb->path.cmd, 0); } return 0; }
static void set_sbuf_protocol1(struct sbuf *sb) { if(sbuf_is_filedata(sb) || sbuf_is_vssdata(sb)) { char *endfile=gen_endfile_str(); iobuf_free_content(&sb->endfile); sb->endfile.cmd=CMD_END_FILE; sb->endfile.len=strlen(endfile); fail_unless((sb->endfile.buf =strdup_w(endfile, __func__))!=NULL); } if(sbuf_is_filedata(sb) || sbuf_is_vssdata(sb)) { char *datapth; fail_unless((datapth=prepend_s(TREE_DIR, sb->path.buf))!=NULL); iobuf_free_content(&sb->protocol1->datapth); iobuf_from_str(&sb->protocol1->datapth, CMD_DATAPTH, datapth); } }
static int process_new(struct sdirs *sdirs, struct conf **cconfs, struct sbuf *p1b, struct manio *ucmanio) { if(!p1b->path.buf) return 0; if(sbuf_is_filedata(p1b) || sbuf_is_vssdata(p1b)) { //logp("need to process new file: %s\n", p1b->path); // Flag the things that need to be sent (to the client) p1b->flags |= SBUF_SEND_STAT; p1b->flags |= SBUF_SEND_PATH; return 0; } return new_non_file(p1b, ucmanio, cconfs); }
void build_manifest_phase2_from_slist(const char *path, struct slist *slist, enum protocol protocol, int short_write) { struct sbuf *sb; struct manio *manio=NULL; for(sb=slist->head; sb; sb=sb->next) set_sbuf(slist, sb, 0 /* with_data_files */); fail_unless((manio=manio_open_phase2(path, "wb", protocol))!=NULL); for(sb=slist->head; sb; sb=sb->next) { fail_unless(!manio_write_sbuf(manio, sb)); if(protocol==PROTO_2) { struct blk *blk=NULL; for(blk=sb->protocol2->bstart; blk && blk!=sb->protocol2->bend; blk=blk->next) { fail_unless(!manio_write_sig_and_path(manio, blk)); } if(sbuf_is_filedata(sb) || sbuf_is_vssdata(sb)) { struct iobuf endfile; iobuf_from_str(&endfile, CMD_END_FILE, (char *)"0:0"); fail_unless(!iobuf_send_msg_fzp(&endfile, manio->fzp)); } hack_protocol2_attr(&sb->attr); } } if(short_write) { man_off_t *pos; fail_unless((pos=manio_tell(manio))!=NULL); if(pos->offset>=short_write) pos->offset-=short_write; fail_unless(!manio_close_and_truncate(&manio, pos, 0 /* compression */)); man_off_t_free(&pos); } fail_unless(!manio_close(&manio)); }
static void set_sbuf_protocol2(struct slist *slist, struct sbuf *sb, int with_data_files) { struct blk *tail=NULL; struct blist *blist=slist->blist; if(sbuf_is_filedata(sb) || sbuf_is_vssdata(sb)) { if(blist->tail) tail=blist->tail; build_blks(blist, prng_next()%50, with_data_files); if(tail) sb->protocol2->bstart=tail->next; else sb->protocol2->bstart=blist->head; // first one if(sb->protocol2->bstart) sb->protocol2->bend=slist->blist->tail; } }
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 maybe_do_delta_stuff(struct asfd *asfd, struct sdirs *sdirs, struct sbuf *cb, struct sbuf *p1b, struct manio *ucmanio, struct conf **cconfs) { int oldcompressed=0; int compression=p1b->compression; // If the file type changed, I think it is time to back it up again // (for example, EFS changing to normal file, or back again). if(cb->path.cmd!=p1b->path.cmd) return process_new_file(sdirs, cconfs, cb, p1b, ucmanio); // mtime is the actual file data. // ctime is the attributes or meta data. if(cb->statp.st_mtime==p1b->statp.st_mtime && cb->statp.st_ctime==p1b->statp.st_ctime) { // got an unchanged file //logp("got unchanged file: %s %c %c\n", // cb->path.buf, cb->path.cmd, p1b->path.cmd); return process_unchanged_file(p1b, cb, ucmanio, cconfs); } if(cb->statp.st_mtime==p1b->statp.st_mtime && cb->statp.st_ctime!=p1b->statp.st_ctime) { // File data stayed the same, but attributes or meta data // changed. We already have the attributes, but may need to get // extra meta data. // FIX THIS horrible mess. if(cb->path.cmd==CMD_ENC_METADATA || p1b->path.cmd==CMD_ENC_METADATA || cb->path.cmd==CMD_EFS_FILE || p1b->path.cmd==CMD_EFS_FILE // FIX THIS: make unencrypted metadata use the librsync || cb->path.cmd==CMD_METADATA || p1b->path.cmd==CMD_METADATA || sbuf_is_vssdata(cb) || sbuf_is_vssdata(p1b)) return process_new_file(sdirs, cconfs, cb, p1b, ucmanio); // On Windows, we have to back up the whole file if ctime // changed, otherwise things like permission changes do not get // noticed. So, in that case, fall through to the changed stuff // below. // Non-Windows clients finish here. else if(!get_int(cconfs[OPT_CLIENT_IS_WINDOWS])) return process_unchanged_file(p1b, cb, ucmanio, cconfs); } // Got a changed file. //logp("got changed file: %s\n", p1b->path.buf); // If either old or new is encrypted, or librsync is off, we need to // get a new file. // FIX THIS horrible mess. if(!get_int(cconfs[OPT_LIBRSYNC]) // FIX THIS: make unencrypted metadata use the librsync || cb->path.cmd==CMD_METADATA || p1b->path.cmd==CMD_METADATA || sbuf_is_encrypted(cb) || sbuf_is_encrypted(p1b) || sbuf_is_vssdata(cb) || sbuf_is_vssdata(p1b)) return process_new_file(sdirs, cconfs, cb, p1b, ucmanio); // Get new files if they have switched between compression on or off. if(cb->protocol1->datapth.buf && dpth_protocol1_is_compressed(cb->compression, cb->protocol1->datapth.buf)) oldcompressed=1; if( ( oldcompressed && !compression) || (!oldcompressed && compression)) return process_new_file(sdirs, cconfs, cb, p1b, ucmanio); // Otherwise, do the delta stuff (if possible). if(sbuf_is_filedata(p1b) || sbuf_is_vssdata(p1b)) { if(process_changed_file(asfd, sdirs, cconfs, cb, p1b, sdirs->currentdata)) return -1; } else { if(changed_non_file(p1b, ucmanio, p1b->path.cmd, cconfs)) return -1; } sbuf_free_content(cb); return 1; }
static enum parse_ret parse_cmd(struct sbuf *sb, struct asfd *asfd, struct iobuf *rbuf, struct blk *blk, struct cntr *cntr) { switch(rbuf->cmd) { case CMD_ATTRIBS: if(sb->protocol2) sbuf_free_content(sb); else { if(sb->protocol1->datapth.buf) // protocol 1 phase 2+ file data // starts with datapth. iobuf_free_content(&sb->attr); else // protocol 1 phase 1 or non file data // starts with attribs sbuf_free_content(sb); } iobuf_move(&sb->attr, rbuf); attribs_decode(sb); return PARSE_RET_NEED_MORE; case CMD_FILE: case CMD_DIRECTORY: case CMD_SOFT_LINK: case CMD_HARD_LINK: case CMD_SPECIAL: // Stuff not currently supported in burp-2, but OK // to find in burp-1. case CMD_ENC_FILE: case CMD_METADATA: case CMD_ENC_METADATA: case CMD_EFS_FILE: case CMD_VSS: case CMD_ENC_VSS: case CMD_VSS_T: case CMD_ENC_VSS_T: if(!sb->attr.buf) { log_and_send(asfd, "read cmd with no attribs"); return PARSE_RET_ERROR; } if(sb->flags & SBUF_NEED_LINK) { if(cmd_is_link(rbuf->cmd)) { iobuf_free_content(&sb->link); iobuf_move(&sb->link, rbuf); sb->flags &= ~SBUF_NEED_LINK; return PARSE_RET_COMPLETE; } else { log_and_send(asfd, "got non-link after link in manifest"); return PARSE_RET_NEED_MORE; } } else { iobuf_free_content(&sb->path); iobuf_move(&sb->path, rbuf); if(cmd_is_link(rbuf->cmd)) { sb->flags |= SBUF_NEED_LINK; return PARSE_RET_NEED_MORE; } else if(sb->protocol1 && sb->protocol1->datapth.buf) { // Protocol1 client restore reads // CMD_APPEND and CMD_END_FILE in the // calling function, so pretend it is // complete if we have the hack flag. if(sb->flags & SBUF_CLIENT_RESTORE_HACK) return PARSE_RET_COMPLETE; return PARSE_RET_NEED_MORE; } return PARSE_RET_COMPLETE; } #ifndef HAVE_WIN32 case CMD_SIG: // Fill in the sig/block, if the caller provided // a pointer for one. Server only. if(!blk) return PARSE_RET_NEED_MORE; // Just fill in the sig details. if(blk_set_from_iobuf_sig_and_savepath(blk, rbuf)) return PARSE_RET_ERROR; blk->got_save_path=1; iobuf_free_content(rbuf); return PARSE_RET_COMPLETE; #endif case CMD_DATA: // Need to write the block to disk. // Client only. if(!blk) return PARSE_RET_NEED_MORE; blk->data=rbuf->buf; blk->length=rbuf->len; rbuf->buf=NULL; return PARSE_RET_COMPLETE; case CMD_MESSAGE: case CMD_WARNING: log_recvd(rbuf, cntr, 1); return PARSE_RET_NEED_MORE; case CMD_GEN: if(!strcmp(rbuf->buf, "restoreend") || !strcmp(rbuf->buf, "phase1end") || !strcmp(rbuf->buf, "backupphase2") // Think these are protocol1 things. || !strcmp(rbuf->buf, "backupend") || !strcmp(rbuf->buf, "estimateend")) return PARSE_RET_FINISHED; iobuf_log_unexpected(rbuf, __func__); return PARSE_RET_ERROR; case CMD_FINGERPRINT: if(blk && blk_set_from_iobuf_fingerprint(blk, rbuf)) return PARSE_RET_ERROR; // Fall through. case CMD_MANIFEST: iobuf_free_content(&sb->path); iobuf_move(&sb->path, rbuf); return PARSE_RET_COMPLETE; case CMD_ERROR: logp("got error: %s\n", rbuf->buf); return PARSE_RET_ERROR; case CMD_DATAPTH: if(!sb->protocol1) { iobuf_log_unexpected(rbuf, __func__); return PARSE_RET_ERROR; } if(sb->flags & SBUF_CLIENT_RESTORE_HACK) { sbuf_free_content(sb); sb->flags |= SBUF_CLIENT_RESTORE_HACK; } else sbuf_free_content(sb); iobuf_move(&sb->protocol1->datapth, rbuf); return PARSE_RET_NEED_MORE; case CMD_END_FILE: iobuf_free_content(&sb->endfile); iobuf_move(&sb->endfile, rbuf); if(sb->protocol1) { if(!sb->attr.buf || !sb->protocol1->datapth.buf || (!sbuf_is_filedata(sb) && !sbuf_is_vssdata(sb))) { logp("got unexpected cmd_endfile"); return PARSE_RET_ERROR; } } return PARSE_RET_COMPLETE; default: iobuf_log_unexpected(rbuf, __func__); return PARSE_RET_ERROR; } logp("Fell out of switch unexpectedly in %s()\n", __func__); return PARSE_RET_ERROR; }
static int restore_stream(struct asfd *asfd, struct sdirs *sdirs, struct slist *slist, struct bu *bu, const char *manifest, regex_t *regex, int srestore, struct conf **cconfs, enum action act, enum cntr_status cntr_status) { int ret=-1; int last_ent_was_dir=0; struct sbuf *sb=NULL; struct iobuf *rbuf=asfd->rbuf; struct manio *manio=NULL; struct blk *blk=NULL; struct sbuf *need_data=NULL; enum protocol protocol=get_protocol(cconfs); struct cntr *cntr=get_cntr(cconfs); if(protocol==PROTO_2) { if(asfd->write_str(asfd, CMD_GEN, "restore_stream") || asfd->read_expect(asfd, CMD_GEN, "restore_stream_ok") || !(blk=blk_alloc())) goto end; } if(!(manio=manio_open(manifest, "rb", protocol)) || !(need_data=sbuf_alloc(protocol)) || !(sb=sbuf_alloc(protocol))) goto end; while(1) { iobuf_free_content(rbuf); if(asfd->as->read_quick(asfd->as)) { logp("read quick error\n"); goto end; } if(rbuf->buf) switch(rbuf->cmd) { case CMD_MESSAGE: case CMD_WARNING: { log_recvd(rbuf, cntr, 0); continue; } case CMD_INTERRUPT: // Client wanted to interrupt the // sending of a file. But if we are // here, we have already moved on. // Ignore. continue; default: iobuf_log_unexpected(rbuf, __func__); goto end; } switch(manio_read_with_blk(manio, sb, need_data->path.buf?blk:NULL, sdirs)) { case 0: break; // Keep going. case 1: ret=0; goto end; // Finished OK. default: goto end; // Error; } if(protocol==PROTO_2) { if(sb->endfile.buf) { sbuf_free_content(sb); continue; } if(blk->data) { if(protocol2_extra_restore_stream_bits(asfd, blk, slist, act, need_data, last_ent_was_dir, cntr)) goto end; continue; } sbuf_free_content(need_data); } if(want_to_restore(srestore, sb, regex, cconfs)) { if(restore_ent(asfd, &sb, slist, bu, act, sdirs, cntr_status, cconfs, need_data, &last_ent_was_dir, manifest)) goto end; } else if(sbuf_is_filedata(sb) || sbuf_is_vssdata(sb)) { // Add it to the list of filedata that was not // restored. struct f_link **bucket=NULL; if(!linkhash_search(&sb->statp, &bucket) && linkhash_add(sb->path.buf, &sb->statp, bucket)) goto end; } sbuf_free_content(sb); } end: blk_free(&blk); sbuf_free(&sb); sbuf_free(&need_data); iobuf_free_content(rbuf); manio_close(&manio); return ret; }
// Used when restoring a hard link that we have not restored the destination // for. Read through the manifest from the beginning and substitute the path // and data to the new location. static int hard_link_substitution(struct asfd *asfd, struct sbuf *sb, struct f_link *lp, struct bu *bu, enum action act, struct sdirs *sdirs, enum cntr_status cntr_status, struct conf **cconfs, const char *manifest, struct slist *slist) { int ret=-1; struct sbuf *need_data=NULL; int last_ent_was_dir=0; struct sbuf *hb=NULL; struct manio *manio=NULL; struct blk *blk=NULL; int pcmp; enum protocol protocol=get_protocol(cconfs); struct cntr *cntr=get_cntr(cconfs); if(!(manio=manio_open(manifest, "rb", protocol)) || !(need_data=sbuf_alloc(protocol)) || !(hb=sbuf_alloc(protocol))) goto end; if(protocol==PROTO_2) { if(!(blk=blk_alloc())) goto end; } while(1) { switch(manio_read_with_blk(manio, hb, need_data->path.buf?blk:NULL, sdirs)) { case 0: break; // Keep going. case 1: ret=0; goto end; // Finished OK. default: goto end; // Error; } if(protocol==PROTO_2) { if(hb->endfile.buf) { sbuf_free_content(hb); continue; } if(blk->data) { if(protocol2_extra_restore_stream_bits(asfd, blk, slist, act, need_data, last_ent_was_dir, cntr)) goto end; continue; } sbuf_free_content(need_data); } pcmp=pathcmp(lp->name, hb->path.buf); if(!pcmp && (sbuf_is_filedata(hb) || sbuf_is_vssdata(hb))) { // Copy the path from sb to hb. free_w(&hb->path.buf); if(!(hb->path.buf=strdup_w(sb->path.buf, __func__))) goto end; // Should now be able to restore the original data // to the new location. ret=restore_sbuf(asfd, hb, bu, act, sdirs, cntr_status, cconfs, need_data, manifest, slist); // May still need to get protocol2 data. if(!ret && need_data->path.buf) continue; break; } sbuf_free_content(hb); // Break out once we have gone past the entry that we are // interested in. if(pcmp<0) break; } end: blk_free(&blk); sbuf_free(&hb); manio_close(&manio); return ret; }