// do_flag: Rename flags or remove flag aliases. // Based on RhostMUSH code. // static bool flag_rename(UTF8 *alias, UTF8 *newname) { int nAlias; bool bValidAlias; UTF8 *pCheckedAlias = MakeCanonicalFlagName(alias, &nAlias, &bValidAlias); if (!bValidAlias) { return false; } UTF8 *pAlias = alloc_sbuf("flag_rename.old"); memcpy(pAlias, pCheckedAlias, nAlias+1); int nNewName; bool bValidNewName; UTF8 *pCheckedNewName = MakeCanonicalFlagName(newname, &nNewName, &bValidNewName); if (!bValidNewName) { free_sbuf(pAlias); return false; } UTF8 *pNewName = alloc_sbuf("flag_rename.new"); memcpy(pNewName, pCheckedNewName, nNewName+1); FLAGNAMEENT *flag1; flag1 = (FLAGNAMEENT *)hashfindLEN(pAlias, nAlias, &mudstate.flags_htab); if (flag1 != NULL) { FLAGNAMEENT *flag2; flag2 = (FLAGNAMEENT *)hashfindLEN(pNewName, nNewName, &mudstate.flags_htab); if (flag2 == NULL) { hashaddLEN(pNewName, nNewName, flag1, &mudstate.flags_htab); if (flag1->flagname != flag1->pOrigName) { MEMFREE(flag1->flagname); } flag1->flagname = StringCloneLen(pNewName, nNewName); free_sbuf(pAlias); free_sbuf(pNewName); return true; } } free_sbuf(pAlias); free_sbuf(pNewName); return false; }
static int process_new_file(struct sbuf *cb, struct sbuf *p1b, FILE *ucfp, const char *currentdata, const char *datadirtmp, const char *deltmppath, struct dpth *dpth, int *resume_partial, struct cntr *cntr, struct config *cconf) { if(process_new(p1b, ucfp, currentdata, datadirtmp, deltmppath, dpth, resume_partial, cntr, cconf)) return -1; free_sbuf(cb); return 1; }
static int process_unchanged_file(struct sbuf *cb, FILE *ucfp, struct cntr *cntr) { if(sbuf_to_manifest(cb, ucfp, NULL)) { free_sbuf(cb); return -1; } else { do_filecounter_same(cntr, cb->cmd); } if(cb->endfile) do_filecounter_bytes(cntr, strtoull(cb->endfile, NULL, 10)); free_sbuf(cb); return 1; }
char *unparse_object( dbref player, dbref target, int obey_myopic ) { char *buf, *fp, *bp; int exam; buf = alloc_lbuf( "unparse_object" ); if( target == NOTHING ) { strcpy( buf, "*NOTHING*" ); } else if( target == HOME ) { strcpy( buf, "*HOME*" ); } else if( target == AMBIGUOUS ) { strcpy( buf, "*VARIABLE*" ); } else if( isGarbage( target ) ) { fp = unparse_flags( player, target ); bp = buf; safe_sprintf( buf, &bp, "*GARBAGE*(#%d%s)", target, fp ); free_sbuf( fp ); } else if( !Good_obj( target ) ) { sprintf( buf, "*ILLEGAL*(#%d)", target ); } else { if( obey_myopic ) { exam = MyopicExam( player, target ); } else { exam = Examinable( player, target ); } if( exam || ( Flags( target ) & ( CHOWN_OK | JUMP_OK | LINK_OK | DESTROY_OK ) ) || ( Flags2( target ) & ABODE ) ) { /* * show everything */ fp = unparse_flags( player, target ); bp = buf; safe_sprintf( buf, &bp, "%s(#%d%s)", Name( target ), target, fp ); free_sbuf( fp ); } else { /* * show only the name. */ strcpy( buf, Name( target ) ); } } return buf; }
static int changed_non_file(struct sbuf *p1b, FILE *ucfp, char cmd, struct cntr *cntr) { // As new_non_file. if(sbuf_to_manifest(p1b, ucfp, NULL)) return -1; else do_filecounter_changed(cntr, cmd); free_sbuf(p1b); return 0; }
static int new_non_file(struct sbuf *p1b, FILE *ucfp, char cmd, struct cntr *cntr) { // Is something that does not need more data backed up. // Like a directory or a link or something like that. // Goes into the unchanged file, so that it does not end up out of // order with normal files, which has to wait around for their data // to turn up. if(sbuf_to_manifest(p1b, ucfp, NULL)) return -1; else do_filecounter(cntr, cmd, 0); free_sbuf(p1b); return 0; }
static void tcache_finish(dbref player) { TCENT *xp; while (tcache_head != NULL) { xp = tcache_head; tcache_head = xp->next; notify_printf(Owner(player), "%s(#%d)} '%s' -> '%s'", Name(player), player, xp->orig, xp->result); free_lbuf(xp->orig); free_lbuf(xp->result); free_sbuf(xp); } tcache_top = 1; tcache_count = 0; }
/* * --------------------------------------------------------------------------- * * Return an lbuf pointing to the object name and possibly the db# and flags */ UTF8 *unparse_object(dbref player, dbref target, bool obey_myopic, bool bAddColor) { UTF8 *buf = alloc_lbuf("unparse_object"); if (NOPERM <= target && target < 0) { mux_strncpy(buf, aszSpecialDBRefNames[-target], LBUF_SIZE-1); } else if (!Good_obj(target)) { mux_sprintf(buf, LBUF_SIZE, T("*ILLEGAL*(#%d)"), target); } else { bool exam; if (obey_myopic) { exam = MyopicExam(player, target); } else { exam = Examinable(player, target); } // Leave and extra 100 bytes for the dbref and flags at the end and // color at the beginning if necessary.. // mux_field fldName = StripTabsAndTruncate( Moniker(target), buf, LBUF_SIZE-100, LBUF_SIZE-100); UTF8 *bp = buf + fldName.m_byte; #if defined(FIRANMUX) if ( fldName.m_column == fldName.m_byte && bAddColor) { // There is no color in the name, so look for @color, or highlight. // UTF8 *buf2 = alloc_lbuf("unparse_object.color"); UTF8 *bp2 = buf2; UTF8 *pLetters = AcquireColorLetters(player, target); if (NULL != pLetters) { safe_str(LettersToBinary(pLetters), buf2, &bp2); free_lbuf(pLetters); pLetters = NULL; } else { safe_str((UTF8 *)COLOR_INTENSE, buf2, &bp2); } *bp = '\0'; safe_str(buf, buf2, &bp2); safe_str((UTF8 *)COLOR_RESET, buf2, &bp2); // Swap buffers. // free_lbuf(buf); buf = buf2; bp = bp2; } #else UNUSED_PARAMETER(bAddColor); #endif // FIRANMUX if ( exam || (Flags(target) & (CHOWN_OK | JUMP_OK | LINK_OK | DESTROY_OK)) || (Flags2(target) & ABODE)) { // Show everything. // UTF8 *fp = decode_flags(player, &(db[target].fs)); safe_str(T("(#"), buf, &bp); safe_ltoa(target, buf, &bp); safe_str(fp, buf, &bp); safe_chr(')', buf, &bp); free_sbuf(fp); } *bp = '\0'; } return buf; }
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; }
/* --------------------------------------------------------------------------- * did_it_rlevel: Have player do something to/with thing, watching the * attributes. 'what' is actually ignored, the desclist match being used * instead. */ void did_it_rlevel ( dbref player, dbref thing, int what, const UTF8 *def, int owhat, const UTF8 *odef, int awhat, int ctrl_flags, const UTF8 *args[], int nargs ) { if (MuxAlarm.bAlarmed) { return; } UTF8 *d, *buff, *act, *charges, *bp; dbref aowner; int num, aflags; int i; bool found_a_desc; reg_ref **preserve = NULL; bool need_pres = false; // Message to player. // if (0 < what) { // Get description list. // DESC_INFO *desclist = desclist_match(player, thing); found_a_desc = false; for (i = 0; i < desclist->n; i++) { // Ok, if it's A_DESC, we need to check against A_IDESC. // if ( A_IDESC == what && A_DESC == desclist->descs[i]) { d = atr_pget(thing, A_IDESC, &aowner, &aflags); } else { d = atr_pget(thing, desclist->descs[i], &aowner, &aflags); } if ('\0' != d[0]) { // No need for the 'def' message. // found_a_desc = true; if (!need_pres) { need_pres = true; preserve = PushRegisters(MAX_GLOBAL_REGS); save_global_regs(preserve); } buff = bp = alloc_lbuf("did_it.1"); mux_exec(d, LBUF_SIZE-1, buff, &bp, thing, thing, player, AttrTrace(aflags, EV_EVAL|EV_FIGNORE|EV_TOP), args, nargs); *bp = '\0'; if ( A_HTDESC == desclist->descs[i] && Html(player)) { safe_str(T("\r\n"), buff, &bp); *bp = '\0'; notify_html(player, buff); } else { notify(player, buff); } free_lbuf(buff); } free_lbuf(d); } if (!found_a_desc) { // No desc found... try the default desc (again). // A_DESC or A_HTDESC... the worst case we look for it twice. // d = atr_pget(thing, what, &aowner, &aflags); if ('\0' != d[0]) { // No need for the 'def' message // found_a_desc = true; if (!need_pres) { need_pres = true; preserve = PushRegisters(MAX_GLOBAL_REGS); save_global_regs(preserve); } buff = bp = alloc_lbuf("did_it.1"); mux_exec(d, LBUF_SIZE-1, buff, &bp, thing, thing, player, AttrTrace(aflags, EV_EVAL|EV_FIGNORE|EV_TOP), args, nargs); *bp = '\0'; if ( A_HTDESC == what && Html(player)) { safe_str(T("\r\n"), buff, &bp); *bp = '\0'; notify_html(player, buff); } else { notify(player, buff); } free_lbuf(buff); } else if (def) { notify(player, def); } free_lbuf(d); } } else if ( what < 0 && def) { notify(player, def); } if (isPlayer(thing)) { d = atr_pget(mudconf.master_room, get_atr(T("ASSET_DESC")), &aowner, &aflags); if (*d) { if (!need_pres) { need_pres = true; preserve = PushRegisters(MAX_GLOBAL_REGS); save_global_regs(preserve); } buff = bp = alloc_lbuf("did_it.1"); mux_exec(d, LBUF_SIZE-1, buff, &bp, thing, thing, player, AttrTrace(aflags, EV_EVAL|EV_FIGNORE|EV_TOP), args, nargs); *bp = '\0'; notify(player, buff); free_lbuf(buff); } free_lbuf(d); } // Message to neighbors. // dbref loc; if ( 0 < owhat && Has_location(player) && Good_obj(loc = Location(player))) { d = atr_pget(thing, owhat, &aowner, &aflags); if (*d) { if (!need_pres) { need_pres = true; preserve = PushRegisters(MAX_GLOBAL_REGS); save_global_regs(preserve); } buff = bp = alloc_lbuf("did_it.2"); mux_exec(d, LBUF_SIZE-1, buff, &bp, thing, thing, player, AttrTrace(aflags, EV_EVAL|EV_FIGNORE|EV_TOP), args, nargs); *bp = '\0'; if (*buff) { if (aflags & AF_NONAME) { notify_except2_rlevel2(loc, player, player, thing, buff); } else { notify_except2_rlevel2(loc, player, player, thing, tprintf(T("%s %s"), Name(player), buff)); } } free_lbuf(buff); } else if (odef) { if (ctrl_flags & VERB_NONAME) { notify_except2_rlevel2(loc, player, player, thing, odef); } else { notify_except2_rlevel2(loc, player, player, thing, tprintf(T("%s %s"), Name(player), odef)); } } free_lbuf(d); } else if ( owhat < 0 && odef && Has_location(player) && Good_obj(loc = Location(player))) { if (ctrl_flags & VERB_NONAME) { notify_except2_rlevel2(loc, player, player, thing, odef); } else { notify_except2_rlevel2(loc, player, player, thing, tprintf(T("%s %s"), Name(player), odef)); } } // If we preserved the state of the global registers, restore them. // if (need_pres) { restore_global_regs(preserve); PopRegisters(preserve, MAX_GLOBAL_REGS); } // Do the action attribute. // if ( awhat > 0 && IsReal(thing, player)) { act = atr_pget(thing, awhat, &aowner, &aflags); if (*act != '\0') { charges = atr_pget(thing, A_CHARGES, &aowner, &aflags); if (*charges) { num = mux_atol(charges); if (num > 0) { buff = alloc_sbuf("did_it.charges"); mux_ltoa(num-1, buff); atr_add_raw(thing, A_CHARGES, buff); free_sbuf(buff); } else { buff = atr_pget(thing, A_RUNOUT, &aowner, &aflags); if (*buff != '\0') { free_lbuf(act); act = buff; } else { free_lbuf(act); free_lbuf(buff); free_lbuf(charges); return; } } } free_lbuf(charges); CLinearTimeAbsolute lta; wait_que(thing, player, player, AttrTrace(aflags, 0), false, lta, NOTHING, 0, act, nargs, args, mudstate.global_regs); } free_lbuf(act); } }
// 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; }
static int list_manifest(const char *fullpath, regex_t *regex, const char *browsedir, const char *client, struct cntr *p1cntr, struct cntr *cntr) { int ars=0; int ret=0; int quit=0; gzFile zp=NULL; struct sbuf mb; char *manifest=NULL; size_t bdlen=0; init_sbuf(&mb); if(!(manifest=prepend_s(fullpath, "manifest.gz", strlen("manifest.gz")))) { log_and_send_oom(__FUNCTION__); return -1; } if(!(zp=gzopen_file(manifest, "rb"))) { log_and_send("could not open manifest"); free(manifest); return -1; } free(manifest); if(browsedir) bdlen=strlen(browsedir); while(!quit) { int show=0; //logp("list manifest loop\n"); // Need to parse while sending, to take note of the regex. free_sbuf(&mb); if((ars=sbuf_fill(NULL, zp, &mb, cntr))) { if(ars<0) ret=-1; // ars==1 means it ended ok. break; } if(mb.cmd!=CMD_DIRECTORY && mb.cmd!=CMD_FILE && mb.cmd!=CMD_ENC_FILE && mb.cmd!=CMD_EFS_FILE && mb.cmd!=CMD_SPECIAL && !cmd_is_link(mb.cmd)) continue; //if(mb.path[mb.plen]=='\n') mb.path[mb.plen]='\0'; write_status(client, STATUS_LISTING, mb.path, p1cntr, cntr); if(browsedir) { int r; if((r=check_browsedir(browsedir, &(mb.path), bdlen))<0) { quit++; ret=-1; } if(!r) continue; show++; } else { if(check_regex(regex, mb.path)) show++; } if(show) { if(async_write(CMD_STAT, mb.statbuf, mb.slen) || async_write(mb.cmd, mb.path, mb.plen)) { quit++; ret=-1; } else if(sbuf_is_link(&mb) && async_write(mb.cmd, mb.linkto, mb.llen)) { quit++; ret=-1; } } } gzclose_fp(&zp); free_sbuf(&mb); return ret; }
static int resume_partial_new_file(struct sbuf *p1b, struct cntr *cntr, const char *currentdata, const char *datadirtmp, const char *deltmppath, struct dpth *dpth, struct config *cconf) { int ret=0; int junk=0; struct sbuf cb; char *rpath=NULL; int istreedata=0; struct stat statp; char *partial=NULL; char *partialdir=NULL; char *zdeltmp=NULL; // It does not matter what this checksum is set to. // This is just to get an endfile string in the format that // process_changed_file expects. unsigned char checksum[18]="0123456789ABCDEF"; // Need to set up a fake current sbuf. init_sbuf(&cb); cb.cmd=p1b->cmd; cb.compression=p1b->compression; cb.path=strdup(p1b->path); cb.statbuf=strdup(p1b->statbuf); if(!(rpath=set_new_datapth(&cb, datadirtmp, dpth, &istreedata, cconf))) { ret=-1; goto end; } if(!(partialdir=prepend_s(datadirtmp, "p", strlen("p"))) || !(partial=prepend_s(partialdir, cb.datapth, strlen(cb.datapth))) || build_path(partialdir, cb.datapth, strlen(cb.datapth), &partial, partialdir)) { ret=-1; goto end; } if(!lstat(partial, &statp) && S_ISREG(statp.st_mode)) { // A previous resume was going on. // Need to concatenate the possible delta onto the partial // file. FILE *dfp=NULL; gzFile dzp=NULL; logp("Resume previously resumed partial new file: %s %s\n", cb.path, rpath); if(!(cb.endfile=strdup( get_endfile_str(statp.st_size, checksum)))) { ret=-1; goto end; } if(cb.compression) { // Recreate partial, in case it was only partially // written and consequently has gz errors. if(!(zdeltmp=prepend(deltmppath, ".z", strlen(".z"), 0 /* no slash */)) || !(dzp=gzopen_file(zdeltmp, "wb")) || copy_gzpath_to_gzFile(partial, dzp) || do_rename(zdeltmp, partial)) { ret=-1; goto end; } } else { // Just append to the existing one. if(!(dfp=open_file(partial, "ab"))) { ret=-1; goto end; } } if(!lstat(deltmppath, &statp) && S_ISREG(statp.st_mode)) { if(cb.compression) { if(copy_gzpath_to_gzFile(deltmppath, dzp)) { ret=-1; goto end; } } else { if(copy_path_to_File(deltmppath, dfp)) { ret=-1; goto end; } } } if(dfp && close_fp(&dfp)) { ret=-1; goto end; } if(dzp && gzclose_fp(&dzp)) { ret=-1; goto end; } if(process_changed_file(&cb, p1b, partialdir, NULL, NULL, &junk /* resume_partial=0 */, cntr, cconf)) { ret=-1; goto end; } if(!istreedata) incr_dpth(dpth, cconf); goto end; } logp("Resume partial new file: %s %s\n", cb.path, rpath); if(!lstat(rpath, &statp) && S_ISREG(statp.st_mode)) { if(!(cb.endfile=strdup( get_endfile_str(statp.st_size, checksum)))) { ret=-1; goto end; } // If compression is on, be careful with gzip unexpected // end of file errors. // Otherwise, just rename the whole file. unlink(partial); if(cb.compression) { if(copy_gzpath_to_gzpath(rpath, partial)) { logp("Error in copy_gzpath_to_gzpath\n"); ret=-1; goto end; } // delete the original. if(unlink(rpath)) { logp("Failed to unlink %s: %s\n", rpath, strerror(errno)); return -1; } } else { if(do_rename(rpath, partial)) { ret=-1; goto end; } } // So, we have created a new directory beginning with 'p', // and moved the partial download to it. // We can now use the partial file as the basis of a librsync // transfer. if(process_changed_file(&cb, p1b, partialdir, NULL, NULL, &junk /* resume_partial=0 */, cntr, cconf)) { ret=-1; goto end; } if(!istreedata) incr_dpth(dpth, cconf); goto end; } logp("Actually, no - just treat it as completely new\n"); end: if(rpath) free(rpath); if(partialdir) free(partialdir); if(partial) free(partial); if(zdeltmp) free(zdeltmp); free_sbuf(&cb); return ret; }
// TODO: Some of the repeated code in this can be factored out. static int resume_partial_changed_file(struct sbuf *cb, struct sbuf *p1b, const char *currentdata, const char *curpath, const char *datadirtmp, const char *deltmppath, struct config *cconf, struct cntr *cntr) { int ret=0; int istreedata=0; struct stat dstatp; struct stat cstatp; char *partial=NULL; char *partialdir=NULL; char *zdeltmp=NULL; struct sbuf xb; init_sbuf(&xb); xb.cmd=cb->cmd; xb.compression=cb->compression; xb.path=strdup(cb->path); xb.statbuf=strdup(cb->statbuf); xb.datapth=strdup(p1b->datapth); xb.endfile=strdup(cb->endfile); logp("Resume partial changed file: %s\n", xb.path); if(!lstat(deltmppath, &dstatp) && S_ISREG(dstatp.st_mode) && !lstat(curpath, &cstatp) && S_ISREG(cstatp.st_mode)) { int junk=0; gzFile dzp=NULL; FILE *dfp=NULL; struct stat pstatp; if(!(partialdir=prepend_s(datadirtmp, "p", strlen("p"))) || !(partial=prepend_s(partialdir, xb.datapth, strlen(xb.datapth))) || build_path(partialdir, xb.datapth, strlen(xb.datapth), &partial, partialdir)) { ret=-1; goto end; } if(!lstat(partial, &pstatp)) { if(!S_ISREG(pstatp.st_mode)) { logp("%s is not a regular file\n", partial); goto actuallyno; } if(pstatp.st_size>cstatp.st_size) { // Looks like a previously resumed file. if(xb.compression) { // Need to read and recreate it, in // case it was not fully created. if(!(zdeltmp=prepend(deltmppath, ".z", strlen(".z"), 0 /* no slash */)) || !(dzp=gzopen_file(zdeltmp, "wb")) || copy_gzpath_to_gzFile(partial, dzp) || do_rename(zdeltmp, partial)) { ret=-1; goto end; } } else { // Append to the existing one. if(!(dfp=open_file(partial, "ab"))) { ret=-1; goto end; } } } else { unlink(partial); // Copy the whole of p1b->sigfp/sigzp to // partial. if(xb.compression) { if(!(dzp=gzopen_file(partial, "wb")) || copy_gzFile_to_gzFile(p1b->sigzp, dzp)) { ret=-1; goto end; } } else { if(!(dfp=open_file(partial, "wb")) || copy_File_to_File(p1b->sigfp, dfp)) { ret=-1; goto end; } } } // Now, copy the whole of deltmppath onto partial. // dzp or dfp will be open by this point. if(xb.compression) { if(copy_gzpath_to_gzFile(deltmppath, dzp)) { ret=-1; goto end; } } else { if(copy_path_to_File(deltmppath, dfp)) { ret=-1; goto end; } } } else { // Copy the whole of p1b->sigfp/sigzp onto partial. // Copy the whole of deltmppath onto partial. if(xb.compression) { // There is no partial, this creates it. if(!(dzp=gzopen_file(partial, "wb")) || copy_gzFile_to_gzFile(p1b->sigzp, dzp)) { ret=-1; goto end; } } else { // There is no partial, this creates it. if(!(dfp=open_file(partial, "wb")) || copy_File_to_File(p1b->sigfp, dfp)) { ret=-1; goto end; } } if(xb.compression) { if(copy_gzpath_to_gzFile(deltmppath, dzp)) { ret=-1; goto end; } } else { if(copy_path_to_File(deltmppath, dfp)) { ret=-1; goto end; } } } if(dfp && close_fp(&dfp)) { ret=-1; goto end; } if(dzp && gzclose_fp(&dzp)) { ret=-1; goto end; } // Use partial as the basis for a librsync transfer. // So, we have created a new directory beginning with 'p', // and moved the partial download to it. // We can now use the partial file as the basis of a librsync // transfer. if(process_changed_file(&xb, p1b, partialdir, NULL, NULL, &junk /* resume_partial=0 */, cntr, cconf)) { ret=-1; goto end; } goto end; } actuallyno: logp("Actually, no - just forget the previous delta\n"); end: if(partialdir) free(partialdir); if(partial) free(partial); if(zdeltmp) free(zdeltmp); free_sbuf(&xb); return ret; }
void exec(char *buff, char **bufc, int tflags, dbref player, dbref cause, int eval, char **dstr, char *cargs[], int ncargs) { #define NFARGS 30 char *fargs[NFARGS]; char *preserve[MAX_GLOBAL_REGS]; char *tstr, *tbuf, *tbufc, *savepos, *atr_gotten, *start, *oldp, *savestr; char savec, ch, *str; char *realbuff = NULL, *realbp = NULL; dbref aowner; int at_space, nfargs, gender, i, j, alldone, feval; long aflags; int is_trace, is_top, save_count; int ansi; FUN *fp; UFUN *ufp; static const char *subj[5] = { "", "it", "she", "he", "they" }; static const char *poss[5] = { "", "its", "her", "his", "their" }; static const char *obj[5] = { "", "it", "her", "him", "them" }; static const char *absp[5] = { "", "its", "hers", "his", "theirs" }; if(*dstr == NULL) return; // dprintk("%d/%s", player, *dstr); at_space = 1; gender = -1; alldone = 0; ansi = 0; is_trace = Trace(player) && !(eval & EV_NOTRACE); is_top = 0; /* Extend the buffer if we need to. */ if(((*bufc) - buff) > (LBUF_SIZE - SBUF_SIZE)) { realbuff = buff; realbp = *bufc; buff = (char *) malloc(LBUF_SIZE); *bufc = buff; } oldp = start = *bufc; /* * If we are tracing, save a copy of the starting buffer */ savestr = NULL; if(is_trace) { is_top = tcache_empty(); savestr = alloc_lbuf("exec.save"); StringCopy(savestr, *dstr); } while (**dstr && !alldone) { switch (**dstr) { case ' ': /* * A space. Add a space if not compressing or if * * * * * * * previous char was not a space */ if(!(mudconf.space_compress && at_space) || (eval & EV_NO_COMPRESS)) { safe_chr(' ', buff, bufc); at_space = 1; } break; case '\\': /* * General escape. Add the following char without * * * * * special processing */ at_space = 0; (*dstr)++; if(**dstr) safe_chr(**dstr, buff, bufc); else (*dstr)--; break; case '[': /* * Function start. Evaluate the contents of the * * * * * square brackets as a function. If no closing * * * * * bracket, insert the [ and continue. */ at_space = 0; tstr = (*dstr)++; if(eval & EV_NOFCHECK) { safe_chr('[', buff, bufc); *dstr = tstr; break; } tbuf = parse_to(dstr, ']', 0); if(*dstr == NULL) { safe_chr('[', buff, bufc); *dstr = tstr; } else { str = tbuf; exec(buff, bufc, 0, player, cause, (eval | EV_FCHECK | EV_FMAND), &str, cargs, ncargs); (*dstr)--; } break; case '{': /* * Literal start. Insert everything up to the * * * * * terminating } without parsing. If no closing * * * * * brace, insert the { and continue. */ at_space = 0; tstr = (*dstr)++; tbuf = parse_to(dstr, '}', 0); if(*dstr == NULL) { safe_chr('{', buff, bufc); *dstr = tstr; } else { if(!(eval & EV_STRIP)) { safe_chr('{', buff, bufc); } /* * Preserve leading spaces (Felan) */ if(*tbuf == ' ') { safe_chr(' ', buff, bufc); tbuf++; } str = tbuf; exec(buff, bufc, 0, player, cause, (eval & ~(EV_STRIP | EV_FCHECK)), &str, cargs, ncargs); if(!(eval & EV_STRIP)) { safe_chr('}', buff, bufc); } (*dstr)--; } break; case '%': /* * Percent-replace start. Evaluate the chars * * * * following * and perform the appropriate * * * * substitution. */ at_space = 0; (*dstr)++; savec = **dstr; savepos = *bufc; switch (savec) { case '\0': /* * Null - all done */ (*dstr)--; break; case '|': /* piped command output */ safe_str(mudstate.pout, buff, bufc); break; case '%': /* * Percent - a literal % */ safe_chr('%', buff, bufc); break; case 'c': case 'C': (*dstr)++; if(!**dstr) (*dstr)--; ansi = 1; switch (**dstr) { case 'h': /* * hilite */ safe_str(ANSI_HILITE, buff, bufc); break; case 'i': /* * inverse */ safe_str(ANSI_INVERSE, buff, bufc); break; case 'f': /* * flash */ safe_str(ANSI_BLINK, buff, bufc); break; case 'u': /* underline */ safe_str(ANSI_UNDER, buff, bufc); break; case 'n': /* * normal */ safe_str(ANSI_NORMAL, buff, bufc); ansi = 0; break; case 'x': /* * black fg */ safe_str(ANSI_BLACK, buff, bufc); break; case 'r': /* * red fg */ safe_str(ANSI_RED, buff, bufc); break; case 'g': /* * green fg */ safe_str(ANSI_GREEN, buff, bufc); break; case 'y': /* * yellow fg */ safe_str(ANSI_YELLOW, buff, bufc); break; case 'b': /* * blue fg */ safe_str(ANSI_BLUE, buff, bufc); break; case 'm': /* * magenta fg */ safe_str(ANSI_MAGENTA, buff, bufc); break; case 'c': /* * cyan fg */ safe_str(ANSI_CYAN, buff, bufc); break; case 'w': /* * white fg */ safe_str(ANSI_WHITE, buff, bufc); break; case 'X': /* * black bg */ safe_str(ANSI_BBLACK, buff, bufc); break; case 'R': /* * red bg */ safe_str(ANSI_BRED, buff, bufc); break; case 'G': /* * green bg */ safe_str(ANSI_BGREEN, buff, bufc); break; case 'Y': /* * yellow bg */ safe_str(ANSI_BYELLOW, buff, bufc); break; case 'B': /* * blue bg */ safe_str(ANSI_BBLUE, buff, bufc); break; case 'M': /* * magenta bg */ safe_str(ANSI_BMAGENTA, buff, bufc); break; case 'C': /* * cyan bg */ safe_str(ANSI_BCYAN, buff, bufc); break; case 'W': /* * white bg */ safe_str(ANSI_BWHITE, buff, bufc); break; default: safe_chr(**dstr, buff, bufc); } break; case 'r': /* * Carriage return */ case 'R': safe_str((char *) "\r\n", buff, bufc); break; case 't': /* * Tab */ case 'T': safe_chr('\t', buff, bufc); break; case 'B': /* * Blank */ case 'b': safe_chr(' ', buff, bufc); break; case '0': /* * Command argument number N */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': i = (**dstr - '0'); if((i < ncargs) && (cargs[i] != NULL)) safe_str(cargs[i], buff, bufc); break; case 'V': /* * Variable attribute */ case 'v': (*dstr)++; ch = ToUpper(**dstr); if(!**dstr) (*dstr)--; if((ch < 'A') || (ch > 'Z')) break; i = 100 + ch - 'A'; atr_gotten = atr_pget(player, i, &aowner, &aflags); safe_str(atr_gotten, buff, bufc); free_lbuf(atr_gotten); break; case 'Q': case 'q': (*dstr)++; i = (**dstr - '0'); if((i >= 0) && (i <= 9) && mudstate.global_regs[i]) { safe_str(mudstate.global_regs[i], buff, bufc); } if(!**dstr) (*dstr)--; break; case 'O': /* * Objective pronoun */ case 'o': if(gender < 0) gender = get_gender(cause); if(!gender) tbuf = Name(cause); else tbuf = (char *) obj[gender]; safe_str(tbuf, buff, bufc); break; case 'P': /* * Personal pronoun */ case 'p': if(gender < 0) gender = get_gender(cause); if(!gender) { safe_str(Name(cause), buff, bufc); safe_chr('s', buff, bufc); } else { safe_str((char *) poss[gender], buff, bufc); } break; case 'S': /* * Subjective pronoun */ case 's': if(gender < 0) gender = get_gender(cause); if(!gender) tbuf = Name(cause); else tbuf = (char *) subj[gender]; safe_str(tbuf, buff, bufc); break; case 'A': /* * Absolute posessive */ case 'a': /* * idea from Empedocles */ if(gender < 0) gender = get_gender(cause); if(!gender) { safe_str(Name(cause), buff, bufc); safe_chr('s', buff, bufc); } else { safe_str((char *) absp[gender], buff, bufc); } break; case '#': /* * Invoker DB number */ tbuf = alloc_sbuf("exec.invoker"); sprintf(tbuf, "#%ld", cause); safe_str(tbuf, buff, bufc); free_sbuf(tbuf); break; case '!': /* * Executor DB number */ tbuf = alloc_sbuf("exec.executor"); sprintf(tbuf, "#%ld", player); safe_str(tbuf, buff, bufc); free_sbuf(tbuf); break; case 'N': /* * Invoker name */ case 'n': safe_str(Name(cause), buff, bufc); break; case 'L': /* * Invoker location db# */ case 'l': if(!(eval & EV_NO_LOCATION)) { tbuf = alloc_sbuf("exec.exloc"); sprintf(tbuf, "#%ld", where_is(cause)); safe_str(tbuf, buff, bufc); free_sbuf(tbuf); } break; default: /* * Just copy */ safe_chr(**dstr, buff, bufc); } if(isupper(savec)) *savepos = ToUpper(*savepos); break; case '(': /* * Arglist start. See if what precedes is a function. If so, * execute it if we should. */ at_space = 0; if(!(eval & EV_FCHECK)) { safe_chr('(', buff, bufc); break; } /* * Load an sbuf with an uppercase version of the func name, and * see if the func exists. Trim trailing spaces from the name * if configured. */ **bufc = '\0'; tbufc = tbuf = alloc_sbuf("exec.tbuf"); safe_sb_str(oldp, tbuf, &tbufc); *tbufc = '\0'; if(mudconf.space_compress) { while ((--tbufc >= tbuf) && isspace(*tbufc)); tbufc++; *tbufc = '\0'; } for(tbufc = tbuf; *tbufc; tbufc++) *tbufc = ToLower(*tbufc); fp = (FUN *) hashfind(tbuf, &mudstate.func_htab); /* * If not a builtin func, check for global func */ ufp = NULL; if(fp == NULL) { ufp = (UFUN *) hashfind(tbuf, &mudstate.ufunc_htab); } /* * Do the right thing if it doesn't exist */ if(!fp && !ufp) { if(eval & EV_FMAND) { *bufc = oldp; safe_str((char *) "#-1 FUNCTION (", buff, bufc); safe_str(tbuf, buff, bufc); safe_str((char *) ") NOT FOUND", buff, bufc); alldone = 1; } else { safe_chr('(', buff, bufc); } free_sbuf(tbuf); eval &= ~EV_FCHECK; break; } free_sbuf(tbuf); /* * Get the arglist and count the number of args * Neg * * * * * * * # of args means catenate subsequent * args */ if(ufp) nfargs = NFARGS; else if(fp->nargs < 0) nfargs = -fp->nargs; else nfargs = NFARGS; tstr = *dstr; if(fp && (fp->flags & FN_NO_EVAL)) feval = (eval & ~EV_EVAL) | EV_STRIP_ESC; else feval = eval; *dstr = parse_arglist(player, cause, *dstr + 1, ')', feval, fargs, nfargs, cargs, ncargs); /* * If no closing delim, just insert the '(' and * * * * * * continue normally */ if(!*dstr) { *dstr = tstr; safe_chr(**dstr, buff, bufc); for(i = 0; i < nfargs; i++) if(fargs[i] != NULL) free_lbuf(fargs[i]); eval &= ~EV_FCHECK; break; } /* * Count number of args returned */ (*dstr)--; j = 0; for(i = 0; i < nfargs; i++) if(fargs[i] != NULL) j = i + 1; nfargs = j; /* * If it's a user-defined function, perform it now. */ if(ufp) { mudstate.func_nest_lev++; if(!check_access(player, ufp->perms)) { safe_str("#-1 PERMISSION DENIED", buff, &oldp); *bufc = oldp; } else { tstr = atr_get(ufp->obj, ufp->atr, &aowner, &aflags); if(ufp->flags & FN_PRIV) i = ufp->obj; else i = player; str = tstr; if(ufp->flags & FN_PRES) { for(j = 0; j < MAX_GLOBAL_REGS; j++) { if(!mudstate.global_regs[j]) preserve[j] = NULL; else { preserve[j] = alloc_lbuf("eval_regs"); StringCopy(preserve[j], mudstate.global_regs[j]); } } } exec(buff, &oldp, 0, i, cause, feval, &str, fargs, nfargs); *bufc = oldp; if(ufp->flags & FN_PRES) { for(j = 0; j < MAX_GLOBAL_REGS; j++) { if(preserve[j]) { if(!mudstate.global_regs[j]) mudstate.global_regs[j] = alloc_lbuf("eval_regs"); StringCopy(mudstate.global_regs[j], preserve[j]); free_lbuf(preserve[j]); } else { if(mudstate.global_regs[j]) *(mudstate.global_regs[i]) = '\0'; } } } free_lbuf(tstr); } /* * Return the space allocated for the args */ mudstate.func_nest_lev--; for(i = 0; i < nfargs; i++) if(fargs[i] != NULL) free_lbuf(fargs[i]); eval &= ~EV_FCHECK; break; } /* * If the number of args is right, perform the func. * Otherwise return an error message. Note * that parse_arglist returns zero args as one * null arg, so we have to handle that case * specially. */ if((fp->nargs == 0) && (nfargs == 1)) { if(!*fargs[0]) { free_lbuf(fargs[0]); fargs[0] = NULL; nfargs = 0; } } if((nfargs == fp->nargs) || (nfargs == -fp->nargs) || (fp->flags & FN_VARARGS)) { /* * Check recursion limit */ mudstate.func_nest_lev++; mudstate.func_invk_ctr++; if(mudstate.func_nest_lev >= mudconf.func_nest_lim) { safe_str("#-1 FUNCTION RECURSION LIMIT EXCEEDED", buff, bufc); } else if(mudstate.func_invk_ctr == mudconf.func_invk_lim) { safe_str("#-1 FUNCTION INVOCATION LIMIT EXCEEDED", buff, bufc); } else if(!check_access(player, fp->perms)) { safe_str("#-1 PERMISSION DENIED", buff, &oldp); *bufc = oldp; } else if(mudstate.func_invk_ctr < mudconf.func_invk_lim) { fp->fun(buff, &oldp, player, cause, fargs, nfargs, cargs, ncargs); *bufc = oldp; } else { **bufc = '\0'; } mudstate.func_nest_lev--; } else { *bufc = oldp; tstr = alloc_sbuf("exec.funcargs"); sprintf(tstr, "%d", fp->nargs); safe_str((char *) "#-1 FUNCTION (", buff, bufc); safe_str((char *) fp->name, buff, bufc); safe_str((char *) ") EXPECTS ", buff, bufc); safe_str(tstr, buff, bufc); safe_str((char *) " ARGUMENTS", buff, bufc); free_sbuf(tstr); } /* * Return the space allocated for the arguments */ for(i = 0; i < nfargs; i++) if(fargs[i] != NULL) free_lbuf(fargs[i]); eval &= ~EV_FCHECK; break; default: /* * A mundane character. Just copy it */ at_space = 0; safe_chr(**dstr, buff, bufc); } (*dstr)++; } /* * If we're eating spaces, and the last thing was a space, eat it * up. Complicated by the fact that at_space is initially * true. So check to see if we actually put something in the * buffer, too. */ if(mudconf.space_compress && at_space && !(eval & EV_NO_COMPRESS) && (start != *bufc)) (*bufc)--; /* * The ansi() function knows how to take care of itself. However, * if the player used a %c sub in the string, and hasn't yet * terminated the color with a %cn yet, we'll have to do it for * them. */ if(ansi == 1) safe_str(ANSI_NORMAL, buff, bufc); **bufc = '\0'; /* * Report trace information */ if(realbuff) { **bufc = '\0'; *bufc = realbp; safe_str(buff, realbuff, bufc); free(buff); buff = realbuff; } if(is_trace) { tcache_add(savestr, start); save_count = tcache_count - mudconf.trace_limit;; if(is_top || !mudconf.trace_topdown) tcache_finish(player); if(is_top && (save_count > 0)) { tbuf = alloc_mbuf("exec.trace_diag"); sprintf(tbuf, "%d lines of trace output discarded.", save_count); notify(player, tbuf); free_mbuf(tbuf); } } }
// a = length of struct bu array // i = position to restore from static int restore_manifest(struct bu *arr, int a, int i, const char *tmppath1, const char *tmppath2, regex_t *regex, enum action act, const char *client, struct cntr *p1cntr, struct cntr *cntr, struct config *cconf, bool all) { int ret=0; gzFile zp=NULL; char *manifest=NULL; char *datadir=NULL; FILE *logfp=NULL; char *logpath=NULL; char *logpathz=NULL; // For sending status information up to the server. char status=STATUS_RESTORING; if(act==ACTION_RESTORE) status=STATUS_RESTORING; else if(act==ACTION_VERIFY) status=STATUS_VERIFYING; if( (act==ACTION_RESTORE && !(logpath=prepend_s(arr[i].path, "restorelog", strlen("restorelog")))) || (act==ACTION_RESTORE && !(logpathz=prepend_s(arr[i].path, "restorelog.gz", strlen("restorelog.gz")))) || (act==ACTION_VERIFY && !(logpath=prepend_s(arr[i].path, "verifylog", strlen("verifylog")))) || (act==ACTION_VERIFY && !(logpathz=prepend_s(arr[i].path, "verifylog.gz", strlen("verifylog.gz")))) || !(manifest=prepend_s(arr[i].path, "manifest.gz", strlen("manifest.gz")))) { log_and_send("out of memory"); ret=-1; } else if(!(logfp=open_file(logpath, "ab")) || set_logfp(logfp)) { char msg[256]=""; snprintf(msg, sizeof(msg), "could not open log file: %s", logpath); log_and_send(msg); ret=-1; } else if(!(zp=gzopen_file(manifest, "rb"))) { log_and_send("could not open manifest"); ret=-1; } else { char cmd; int quit=0; size_t len=0; struct sbuf sb; // For out-of-sequence directory restoring so that the // timestamps come out right: int s=0; int scount=0; struct sbuf **sblist=NULL; init_sbuf(&sb); while(!quit) { int ars=0; char *buf=NULL; if(async_read_quick(&cmd, &buf, &len)) { logp("read quick error\n"); ret=-1; quit++; break; } if(buf) { //logp("got read quick\n"); if(cmd==CMD_WARNING) { logp("WARNING: %s\n", buf); do_filecounter(cntr, cmd, 0); free(buf); buf=NULL; continue; } else if(cmd==CMD_INTERRUPT) { // Client wanted to interrupt the // sending of a file. But if we are // here, we have already moved on. // Ignore. free(buf); buf=NULL; continue; } else { logp("unexpected cmd from client: %c:%s\n", cmd, buf); free(buf); buf=NULL; ret=-1; quit++; break; } } if((ars=sbuf_fill(NULL, zp, &sb, cntr))) { if(ars<0) ret=-1; // ars==1 means end ok quit++; } else { if(check_regex(regex, sb.path)) { // Check if we have any directories waiting // to be restored. for(s=scount-1; s>=0; s--) { if(is_subdir(sblist[s]->path, sb.path)) { // We are still in a subdir. //printf(" subdir (%s %s)\n", sblist[s]->path, sb.path); break; } else { // Can now restore sblist[s] // because nothing else is // fiddling in a subdirectory. if(restore_sbuf(sblist[s], arr, a, i, tmppath1, tmppath2, act, client, status, p1cntr, cntr, cconf)) { ret=-1; quit++; break; } else if(del_from_sbuf_arr( &sblist, &scount)) { ret=-1; quit++; break; } } } /* If it is a directory, need to remember it and restore it later, so that the permissions come out right. */ /* Meta data of directories will also have the stat stuff set to be a directory, so will also come out at the end. */ if(!ret && S_ISDIR(sb.statp.st_mode)) { if(add_to_sbuf_arr(&sblist, &sb, &scount)) { ret=-1; quit++; } // Wipe out sb, without freeing up // all the strings inside it, which // have been added to sblist. init_sbuf(&sb); } else if(!ret && restore_sbuf(&sb, arr, a, i, tmppath1, tmppath2, act, client, status, p1cntr, cntr, cconf)) { ret=-1; quit++; } } } free_sbuf(&sb); } gzclose_fp(&zp); // Restore any directories that are left in the list. if(!ret) for(s=scount-1; s>=0; s--) { if(restore_sbuf(sblist[s], arr, a, i, tmppath1, tmppath2, act, client, status, p1cntr, cntr, cconf)) { ret=-1; break; } } free_sbufs(sblist, scount); if(!ret && !all) ret=do_restore_end(act, cntr); print_endcounter(cntr); print_filecounters(p1cntr, cntr, act, 0); reset_filecounter(p1cntr); reset_filecounter(cntr); } set_logfp(NULL); compress_file(logpath, logpathz, cconf); if(manifest) free(manifest); if(datadir) free(datadir); if(logpath) free(logpath); if(logpathz) free(logpathz); return ret; }
// return 1 to say that a file was processed static int maybe_process_file(struct sbuf *cb, struct sbuf *p1b, FILE *ucfp, const char *currentdata, const char *datadirtmp, const char *deltmppath, struct dpth *dpth, int *resume_partial, struct cntr *cntr, struct config *cconf) { int pcmp; // logp("in maybe_proc %s\n", p1b->path); if(!(pcmp=sbuf_pathcmp(cb, p1b))) { int oldcompressed=0; // 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->cmd!=p1b->cmd) return process_new_file(cb, p1b, ucfp, currentdata, datadirtmp, deltmppath, dpth, resume_partial, cntr, cconf); // 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, cb->cmd, p1b->cmd); return process_unchanged_file(cb, ucfp, cntr); } 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. if(cb->cmd==CMD_ENC_METADATA || p1b->cmd==CMD_ENC_METADATA // TODO: make unencrypted metadata use the librsync || cb->cmd==CMD_METADATA || p1b->cmd==CMD_METADATA || cb->cmd==CMD_VSS || p1b->cmd==CMD_VSS || cb->cmd==CMD_ENC_VSS || p1b->cmd==CMD_ENC_VSS || cb->cmd==CMD_VSS_T || p1b->cmd==CMD_VSS_T || cb->cmd==CMD_ENC_VSS_T || p1b->cmd==CMD_ENC_VSS_T || cb->cmd==CMD_EFS_FILE || p1b->cmd==CMD_EFS_FILE) return process_new_file(cb, p1b, ucfp, currentdata, datadirtmp, deltmppath, dpth, resume_partial, cntr, cconf); else return process_unchanged_file(cb, ucfp, cntr); } // Got a changed file. //logp("got changed file: %s\n", p1b->path); // If either old or new is encrypted, or librsync is off, // we need to get a new file. if(!cconf->librsync || cb->cmd==CMD_ENC_FILE || p1b->cmd==CMD_ENC_FILE || cb->cmd==CMD_ENC_METADATA || p1b->cmd==CMD_ENC_METADATA || cb->cmd==CMD_EFS_FILE || p1b->cmd==CMD_EFS_FILE // TODO: make unencrypted metadata use the librsync || cb->cmd==CMD_METADATA || p1b->cmd==CMD_METADATA || cb->cmd==CMD_VSS || p1b->cmd==CMD_VSS || cb->cmd==CMD_ENC_VSS || p1b->cmd==CMD_ENC_VSS || cb->cmd==CMD_VSS_T || p1b->cmd==CMD_VSS_T || cb->cmd==CMD_ENC_VSS_T || p1b->cmd==CMD_ENC_VSS_T) return process_new_file(cb, p1b, ucfp, currentdata, datadirtmp, deltmppath, dpth, resume_partial, cntr, cconf); // Get new files if they have switched between compression on // or off. if(cb->datapth && dpth_is_compressed(cb->compression, cb->datapth)) oldcompressed=1; if( ( oldcompressed && !cconf->compression) || (!oldcompressed && cconf->compression)) return process_new_file(cb, p1b, ucfp, currentdata, datadirtmp, deltmppath, dpth, resume_partial, cntr, cconf); // Otherwise, do the delta stuff (if possible). if(filedata(p1b->cmd)) { if(process_changed_file(cb, p1b, currentdata, datadirtmp, deltmppath, resume_partial, cntr, cconf)) return -1; } else { if(changed_non_file(p1b, ucfp, p1b->cmd, cntr)) return -1; } free_sbuf(cb); return 1; } else if(pcmp>0) { //logp("ahead: %s\n", p1b->path); // ahead - need to get the whole file if(process_new(p1b, ucfp, currentdata, datadirtmp, deltmppath, dpth, resume_partial, cntr, cconf)) return -1; // do not free return 1; } else { //logp("behind: %s\n", p1b->path); // behind - need to read more from the old // manifest // Count a deleted file - it was in the old manifest but not // the new. do_filecounter_deleted(cntr, cb->cmd); } return 0; }
// a = length of struct bu array // i = position to restore from static int restore_manifest(struct bu *arr, int a, int i, const char *tmppath1, const char *tmppath2, regex_t *regex, int srestore, enum action act, const char *client, char **dir_for_notify, struct cntr *p1cntr, struct cntr *cntr, struct config *cconf) { int ret=0; gzFile zp=NULL; char *manifest=NULL; char *datadir=NULL; FILE *logfp=NULL; char *logpath=NULL; char *logpathz=NULL; // For sending status information up to the server. char status=STATUS_RESTORING; if(act==ACTION_RESTORE) status=STATUS_RESTORING; else if(act==ACTION_VERIFY) status=STATUS_VERIFYING; if( (act==ACTION_RESTORE && !(logpath=prepend_s(arr[i].path, "restorelog", strlen("restorelog")))) || (act==ACTION_RESTORE && !(logpathz=prepend_s(arr[i].path, "restorelog.gz", strlen("restorelog.gz")))) || (act==ACTION_VERIFY && !(logpath=prepend_s(arr[i].path, "verifylog", strlen("verifylog")))) || (act==ACTION_VERIFY && !(logpathz=prepend_s(arr[i].path, "verifylog.gz", strlen("verifylog.gz")))) || !(manifest=prepend_s(arr[i].path, "manifest.gz", strlen("manifest.gz")))) { log_and_send_oom(__FUNCTION__); ret=-1; } else if(!(logfp=open_file(logpath, "ab")) || set_logfp(logfp, cconf)) { char msg[256]=""; snprintf(msg, sizeof(msg), "could not open log file: %s", logpath); log_and_send(msg); ret=-1; } *dir_for_notify=strdup(arr[i].path); log_restore_settings(cconf, srestore); // First, do a pass through the manifest to set up the counters. // This is the equivalent of a phase1 scan during backup. if(!ret && !(zp=gzopen_file(manifest, "rb"))) { log_and_send("could not open manifest"); ret=-1; } else { int ars=0; int quit=0; struct sbuf sb; init_sbuf(&sb); while(!quit) { if((ars=sbuf_fill(NULL, zp, &sb, cntr))) { if(ars<0) ret=-1; // ars==1 means end ok quit++; } else { if((!srestore || check_srestore(cconf, sb.path)) && check_regex(regex, sb.path)) { do_filecounter(p1cntr, sb.cmd, 0); if(sb.endfile) do_filecounter_bytes(p1cntr, strtoull(sb.endfile, NULL, 10)); /* if(sb.cmd==CMD_FILE || sb.cmd==CMD_ENC_FILE || sb.cmd==CMD_METADATA || sb.cmd==CMD_ENC_METADATA || sb.cmd==CMD_VSS || sb.cmd==CMD_ENC_VSS || sb.cmd==CMD_VSS_T || sb.cmd==CMD_ENC_VSS_T || sb.cmd==CMD_EFS_FILE) do_filecounter_bytes(p1cntr, (unsigned long long) sb.statp.st_size); */ } } free_sbuf(&sb); } free_sbuf(&sb); gzclose_fp(&zp); } if(cconf->send_client_counters) { if(send_counters(client, p1cntr, cntr)) { ret=-1; } } // Now, do the actual restore. if(!ret && !(zp=gzopen_file(manifest, "rb"))) { log_and_send("could not open manifest"); ret=-1; } else { char cmd; int s=0; int quit=0; size_t len=0; struct sbuf sb; // For out-of-sequence directory restoring so that the // timestamps come out right: int scount=0; struct sbuf **sblist=NULL; init_sbuf(&sb); while(!quit) { int ars=0; char *buf=NULL; if(async_read_quick(&cmd, &buf, &len)) { logp("read quick error\n"); ret=-1; quit++; break; } if(buf) { //logp("got read quick\n"); if(cmd==CMD_WARNING) { logp("WARNING: %s\n", buf); do_filecounter(cntr, cmd, 0); free(buf); buf=NULL; continue; } else if(cmd==CMD_INTERRUPT) { // Client wanted to interrupt the // sending of a file. But if we are // here, we have already moved on. // Ignore. free(buf); buf=NULL; continue; } else { logp("unexpected cmd from client: %c:%s\n", cmd, buf); free(buf); buf=NULL; ret=-1; quit++; break; } } if((ars=sbuf_fill(NULL, zp, &sb, cntr))) { if(ars<0) ret=-1; // ars==1 means end ok quit++; } else { if((!srestore || check_srestore(cconf, sb.path)) && check_regex(regex, sb.path) && restore_ent(client, &sb, &sblist, &scount, arr, a, i, tmppath1, tmppath2, act, status, cconf, cntr, p1cntr)) { ret=-1; quit++; } } free_sbuf(&sb); } gzclose_fp(&zp); // Restore any directories that are left in the list. if(!ret) for(s=scount-1; s>=0; s--) { if(restore_sbuf(sblist[s], arr, a, i, tmppath1, tmppath2, act, client, status, p1cntr, cntr, cconf)) { ret=-1; break; } } free_sbufs(sblist, scount); if(!ret) ret=do_restore_end(act, cntr); //print_endcounter(cntr); print_filecounters(p1cntr, cntr, act); reset_filecounter(p1cntr, time(NULL)); reset_filecounter(cntr, time(NULL)); } set_logfp(NULL, cconf); compress_file(logpath, logpathz, cconf); if(manifest) free(manifest); if(datadir) free(datadir); if(logpath) free(logpath); if(logpathz) free(logpathz); return ret; }
static int maybe_delete_files_from_manifest(const char *manifest, const char *deletionsfile, struct config *cconf, struct cntr *cntr) { int ars=0; int ret=0; int pcmp=0; FILE *dfp=NULL; struct sbuf db; struct sbuf mb; gzFile nmzp=NULL; gzFile omzp=NULL; char *manifesttmp=NULL; struct stat statp; if(lstat(deletionsfile, &statp)) { // No deletions, no problem. return 0; } logp("Performing deletions on manifest\n"); if(!(manifesttmp=get_tmp_filename(manifest))) { ret=-1; goto end; } if(!(dfp=open_file(deletionsfile, "rb")) || !(omzp=gzopen_file(manifest, "rb")) || !(nmzp=gzopen_file(manifesttmp, comp_level(cconf)))) { ret=-1; goto end; } init_sbuf(&db); init_sbuf(&mb); while(omzp || dfp) { if(dfp && !db.path && (ars=sbuf_fill(dfp, NULL, &db, cntr))) { if(ars<0) { ret=-1; break; } // ars==1 means it ended ok. close_fp(&dfp); } if(omzp && !mb.path && (ars=sbuf_fill(NULL, omzp, &mb, cntr))) { if(ars<0) { ret=-1; break; } // ars==1 means it ended ok. gzclose_fp(&omzp); } if(mb.path && !db.path) { if(sbuf_to_manifest(&mb, NULL, nmzp)) { ret=-1; break; } free_sbuf(&mb); } else if(!mb.path && db.path) { free_sbuf(&db); } else if(!mb.path && !db.path) { continue; } else if(!(pcmp=sbuf_pathcmp(&mb, &db))) { // They were the same - do not write. free_sbuf(&mb); free_sbuf(&db); } else if(pcmp<0) { // Behind in manifest. Write. if(sbuf_to_manifest(&mb, NULL, nmzp)) { ret=-1; break; } free_sbuf(&mb); } else { // Behind in deletions file. Do not write. free_sbuf(&db); } } end: if(gzclose_fp(&nmzp)) { logp("error closing %s in maybe_delete_files_from_manifest\n", manifesttmp); ret=-1; } close_fp(&dfp); gzclose_fp(&omzp); free_sbuf(&db); free_sbuf(&mb); if(!ret) { unlink(deletionsfile); if(do_rename(manifesttmp, manifest)) { free(manifesttmp); return -1; } } if(manifesttmp) { unlink(manifesttmp); free(manifesttmp); } 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; }
/* Need to make all the stuff that this does atomic so that existing backups never get broken, even if somebody turns the power off on the server. */ static int atomic_data_jiggle(const char *finishing, const char *working, const char *manifest, const char *current, const char *currentdata, const char *datadir, const char *datadirtmp, struct config *cconf, const char *client, int hardlinked, unsigned long bno, struct cntr *p1cntr, struct cntr *cntr) { int ret=0; int ars=0; char *datapth=NULL; char *tmpman=NULL; struct stat statp; char *deltabdir=NULL; char *deltafdir=NULL; char *sigpath=NULL; gzFile zp=NULL; struct sbuf sb; logp("Doing the atomic data jiggle...\n"); if(!(tmpman=get_tmp_filename(manifest))) return -1; if(lstat(manifest, &statp)) { // Manifest does not exist - maybe the server was killed before // it could be renamed. logp("%s did not exist - trying %s\n", manifest, tmpman); do_rename(tmpman, manifest); } free(tmpman); if(!(zp=gzopen_file(manifest, "rb"))) return -1; if(!(deltabdir=prepend_s(current, "deltas.reverse", strlen("deltas.reverse"))) || !(deltafdir=prepend_s(finishing, "deltas.forward", strlen("deltas.forward"))) || !(sigpath=prepend_s(current, "sig.tmp", strlen("sig.tmp")))) { logp("out of memory\n"); gzclose_fp(&zp); return -1; } mkdir(datadir, 0777); init_sbuf(&sb); while(!(ars=sbuf_fill(NULL, zp, &sb, cntr))) { if(sb.datapth) { write_status(client, STATUS_SHUFFLING, sb.datapth, p1cntr, cntr); if((ret=jiggle(sb.datapth, currentdata, datadirtmp, datadir, deltabdir, deltafdir, sigpath, sb.endfile, hardlinked, sb.compression, cntr, cconf))) break; } free_sbuf(&sb); } if(!ret) { if(ars>0) ret=0; else ret=-1; } gzclose_fp(&zp); if(ret) { // Remove the temporary data directory, we have now removed // everything useful from it. sync(); // try to help CIFS recursive_delete(deltafdir, NULL, FALSE /* do not del files */); } if(deltabdir) free(deltabdir); if(deltafdir) free(deltafdir); if(sigpath) free(sigpath); if(datapth) free(datapth); 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; }
// 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; }