static int maybe_do_delta_stuff(struct asfd *asfd, struct sdirs *sdirs, struct sbuf *cb, struct sbuf *p1b, struct fzp *ucfp, struct conf **cconfs) { int oldcompressed=0; int compression=get_int(cconfs[OPT_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, ucfp); // 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, ucfp, 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 // FIX THIS: make unencrypted metadata use the librsync || cb->path.cmd==CMD_METADATA || p1b->path.cmd==CMD_METADATA || cb->path.cmd==CMD_VSS || p1b->path.cmd==CMD_VSS || cb->path.cmd==CMD_ENC_VSS || p1b->path.cmd==CMD_ENC_VSS || cb->path.cmd==CMD_VSS_T || p1b->path.cmd==CMD_VSS_T || cb->path.cmd==CMD_ENC_VSS_T || p1b->path.cmd==CMD_ENC_VSS_T || cb->path.cmd==CMD_EFS_FILE || p1b->path.cmd==CMD_EFS_FILE) return process_new_file(sdirs, cconfs, cb, p1b, ucfp); // 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, ucfp, cconfs); } // 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. // FIX THIS horrible mess. if(get_int(cconfs[OPT_LIBRSYNC]) || cb->path.cmd==CMD_ENC_FILE || p1b->path.cmd==CMD_ENC_FILE || 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 || cb->path.cmd==CMD_VSS || p1b->path.cmd==CMD_VSS || cb->path.cmd==CMD_ENC_VSS || p1b->path.cmd==CMD_ENC_VSS || cb->path.cmd==CMD_VSS_T || p1b->path.cmd==CMD_VSS_T || cb->path.cmd==CMD_ENC_VSS_T || p1b->path.cmd==CMD_ENC_VSS_T) return process_new_file(sdirs, cconfs, cb, p1b, ucfp); // 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, ucfp); // Otherwise, do the delta stuff (if possible). if(cmd_is_filedata(p1b->path.cmd)) { if(process_changed_file(asfd, sdirs, cconfs, cb, p1b, sdirs->currentdata)) return -1; } else { if(changed_non_file(p1b, ucfp, p1b->path.cmd, cconfs)) return -1; } sbuf_free_content(cb); return 1; }
// 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; }
// 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; }
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; }
// return 1 to say that a file was processed static int maybe_process_file(struct asfd *asfd, struct sdirs *sdirs, struct conf *cconf, struct sbuf *cb, struct sbuf *p1b, FILE *ucfp, struct dpthl *dpthl) { int pcmp; 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->path.cmd!=p1b->path.cmd) return process_new_file(sdirs, cconf, cb, p1b, ucfp, dpthl); // 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, cconf); } 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->path.cmd==CMD_ENC_METADATA || p1b->path.cmd==CMD_ENC_METADATA // TODO: make unencrypted metadata use the librsync || cb->path.cmd==CMD_METADATA || p1b->path.cmd==CMD_METADATA || cb->path.cmd==CMD_VSS || p1b->path.cmd==CMD_VSS || cb->path.cmd==CMD_ENC_VSS || p1b->path.cmd==CMD_ENC_VSS || cb->path.cmd==CMD_VSS_T || p1b->path.cmd==CMD_VSS_T || cb->path.cmd==CMD_ENC_VSS_T || p1b->path.cmd==CMD_ENC_VSS_T || cb->path.cmd==CMD_EFS_FILE || p1b->path.cmd==CMD_EFS_FILE) return process_new_file(sdirs, cconf, cb, p1b, ucfp, dpthl); // 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(!cconf->client_is_windows) return process_unchanged_file(cb, ucfp, cconf); } // 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->path.cmd==CMD_ENC_FILE || p1b->path.cmd==CMD_ENC_FILE || cb->path.cmd==CMD_ENC_METADATA || p1b->path.cmd==CMD_ENC_METADATA || cb->path.cmd==CMD_EFS_FILE || p1b->path.cmd==CMD_EFS_FILE // TODO: make unencrypted metadata use the librsync || cb->path.cmd==CMD_METADATA || p1b->path.cmd==CMD_METADATA || cb->path.cmd==CMD_VSS || p1b->path.cmd==CMD_VSS || cb->path.cmd==CMD_ENC_VSS || p1b->path.cmd==CMD_ENC_VSS || cb->path.cmd==CMD_VSS_T || p1b->path.cmd==CMD_VSS_T || cb->path.cmd==CMD_ENC_VSS_T || p1b->path.cmd==CMD_ENC_VSS_T) return process_new_file(sdirs, cconf, cb, p1b, ucfp, dpthl); // Get new files if they have switched between compression on // or off. if(cb->burp1->datapth.buf && dpthl_is_compressed(cb->compression, cb->burp1->datapth.buf)) oldcompressed=1; if( ( oldcompressed && !cconf->compression) || (!oldcompressed && cconf->compression)) return process_new_file(sdirs, cconf, cb, p1b, ucfp, dpthl); // Otherwise, do the delta stuff (if possible). if(filedata(p1b->path.cmd)) { if(process_changed_file(asfd, sdirs, cconf, cb, p1b, sdirs->currentdata)) return -1; } else { if(changed_non_file(p1b, ucfp, p1b->path.cmd, cconf)) return -1; } sbuf_free_content(cb); return 1; } else if(pcmp>0) { //logp("ahead: %s\n", p1b->path); // ahead - need to get the whole file if(process_new(sdirs, cconf, p1b, ucfp, dpthl)) 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. cntr_add_deleted(cconf->cntr, cb->path.cmd); } return 0; }