Here's a patch against rsync 2.5.6 that implements --dest-filter (discussed as --remotefilter) and --times-only. If anyone cares to try it out and let me know if it works on your system, I'd appreciate it. I'll be distributing this patch with my snapshot utilities.
*** rsync-2.5.6/generator.c Thu Aug 29 07:44:55 2002 --- rsync-2.5.6.patched/generator.c Tue Apr 1 11:18:49 2003 *************** *** 35,40 **** --- 35,42 ---- extern int block_size; extern int csum_length; extern int ignore_times; + extern int times_only; + extern char *dest_filter; extern int size_only; extern int io_timeout; extern int remote_version; *************** *** 48,55 **** static int skip_file(char *fname, struct file_struct *file, STRUCT_STAT *st) { ! if (st->st_size != file->length) { ! return 0; } if (link_dest) { if((st->st_mode & ~_S_IFMT) != (file->mode & ~_S_IFMT)) { --- 50,59 ---- static int skip_file(char *fname, struct file_struct *file, STRUCT_STAT *st) { ! if (! times_only) { ! if (st->st_size != file->length) { ! return 0; ! } } if (link_dest) { if((st->st_mode & ~_S_IFMT) != (file->mode & ~_S_IFMT)) { *************** *** 58,63 **** --- 62,70 ---- if (st->st_uid != file->uid || st->st_gid != file->gid) { return 0; } + } + if (times_only) { + return (cmp_modtime(st->st_mtime,file->modtime) == 0); } *** rsync-2.5.6/options.c Mon Jan 27 19:11:57 2003 --- rsync-2.5.6.patched/options.c Tue Apr 1 22:00:58 2003 *************** *** 48,53 **** --- 48,55 ---- int dry_run=0; int local_server=0; int ignore_times=0; + char *dest_filter = NULL; + int times_only=0; int delete_mode=0; int delete_excluded=0; int one_file_system=0; *************** *** 207,212 **** --- 209,215 ---- rprintf(F," -v, --verbose increase verbosity\n"); rprintf(F," -q, --quiet decrease verbosity\n"); rprintf(F," -c, --checksum always checksum\n"); + rprintf(F," --dest-filter=COMMAND filter file through COMMAND at destination\n"); rprintf(F," -a, --archive archive mode, equivalent to -rlptgoD\n"); rprintf(F," -r, --recursive recurse into directories\n"); rprintf(F," -R, --relative use relative path names\n"); *************** *** 246,251 **** --- 249,255 ---- rprintf(F," --timeout=TIME set IO timeout in seconds\n"); rprintf(F," -I, --ignore-times don't exclude files that match length and time\n"); rprintf(F," --size-only only use file size when determining if a file should be transferred\n"); + rprintf(F," --times-only only use file modification time when determining if a file should be transferred\n"); rprintf(F," --modify-window=NUM Timestamp window (seconds) for file match (default=%d)\n",modify_window); rprintf(F," -T --temp-dir=DIR create temporary files in directory DIR\n"); rprintf(F," --compare-dest=DIR also compare destination files relative to DIR\n"); *************** *** 283,288 **** --- 287,293 ---- } enum {OPT_VERSION = 1000, OPT_SUFFIX, OPT_SENDER, OPT_SERVER, OPT_EXCLUDE, + OPT_DEST_FILTER, OPT_EXCLUDE_FROM, OPT_DELETE, OPT_DELETE_EXCLUDED, OPT_NUMERIC_IDS, OPT_RSYNC_PATH, OPT_FORCE, OPT_TIMEOUT, OPT_DAEMON, OPT_CONFIG, OPT_PORT, OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_STATS, OPT_PARTIAL, OPT_PROGRESS, *************** *** 300,305 **** --- 305,312 ---- {"rsync-path", 0, POPT_ARG_STRING, &rsync_path, 0, 0, 0 }, {"password-file", 0, POPT_ARG_STRING, &password_file, 0, 0, 0 }, {"ignore-times", 'I', POPT_ARG_NONE, &ignore_times , 0, 0, 0 }, + {"times-only", 0, POPT_ARG_NONE, ×_only , 0, 0, 0 }, + {"dest-filter", 0, POPT_ARG_STRING, &dest_filter , OPT_DEST_FILTER, 0, 0 }, {"size-only", 0, POPT_ARG_NONE, &size_only , 0, 0, 0 }, {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 }, {"one-file-system", 'x', POPT_ARG_NONE, &one_file_system , 0, 0, 0 }, *************** *** 471,476 **** --- 478,488 ---- print_rsync_version(FINFO); exit_cleanup(0); + case OPT_DEST_FILTER: + /* dest_filter already set by popt */ + whole_file = 1; + break; + case OPT_SUFFIX: /* The value has already been set by popt, but * we need to remember that a suffix was specified *************** *** 631,636 **** --- 643,655 ---- return 0; } + if (dest_filter && no_whole_file) { + snprintf(err_buf,sizeof(err_buf), + "no-whole-file can not be used with dest-filter\n"); + rprintf(FERROR,"ERROR: no-whole-file can not be used with dest-filter\n"); + return 0; + } + *argv = poptGetArgs(pc); if (*argv) *argc = count_args(*argv); *************** *** 783,788 **** --- 802,818 ---- if (delete_excluded) args[ac++] = "--delete-excluded"; + + if (times_only) + args[ac++] = "--times-only"; + + if (dest_filter) { + static char buf[1000]; + /* have to single quote the arg to keep the + remote shell from splitting it */ + snprintf(buf, sizeof(buf), "--dest-filter='%s'", dest_filter); + args[ac++] = buf; + } if (size_only) args[ac++] = "--size-only"; *** rsync-2.5.6/pipe.c Mon Apr 8 00:39:56 2002 --- rsync-2.5.6.patched/pipe.c Tue Apr 1 21:13:07 2003 *************** *** 146,148 **** --- 146,197 ---- } + pid_t run_filter(char *command[], int out, int *pipe_to_filter) + { + pid_t pid; + int pipefds[2]; + extern int blocking_io; + + if (verbose >= 2) { + print_child_argv(command); + } + + if (pipe(pipefds) < 0) { + rprintf(FERROR, "pipe: %s\n", strerror(errno)); + exit_cleanup(RERR_IPC); + } + + pid = do_fork(); + if (pid == -1) { + rprintf(FERROR, "fork: %s\n", strerror(errno)); + exit_cleanup(RERR_IPC); + } + + if (pid == 0) { + extern int orig_umask; + if (dup2(pipefds[0], STDIN_FILENO) < 0) { + rprintf(FERROR, "Failed dup2 to child stdin : %s\n", + strerror(errno)); + exit_cleanup(RERR_IPC); + } + if (dup2(out, STDOUT_FILENO) < 0) { + rprintf(FERROR, "Failed dup2 to child stdout : %s\n", + strerror(errno)); + exit_cleanup(RERR_IPC); + } + close(pipefds[1]); + umask(orig_umask); + set_blocking(STDIN_FILENO); + if (blocking_io) { + set_blocking(STDOUT_FILENO); + } + execvp(command[0], command); + rprintf(FERROR, "Failed to exec %s : %s\n", + command[0], strerror(errno)); + exit_cleanup(RERR_IPC); + } + + *pipe_to_filter = pipefds[1]; + + return pid; + } *** rsync-2.5.6/proto.h Sun Jan 26 19:35:09 2003 --- rsync-2.5.6.patched/proto.h Tue Apr 1 21:12:49 2003 *************** *** 181,186 **** --- 181,187 ---- pid_t piped_child(char **command, int *f_in, int *f_out); pid_t local_child(int argc, char **argv,int *f_in,int *f_out, int (*child_main)(int, char*[])); + pid_t run_filter(char *command[], int in, int *pipe_to_filter); void end_progress(OFF_T size); void show_progress(OFF_T ofs, OFF_T size); void delete_files(struct file_list *flist); *** rsync-2.5.6/receiver.c Mon Jan 20 15:32:17 2003 --- rsync-2.5.6.patched/receiver.c Tue Apr 1 22:41:13 2003 *************** *** 320,330 **** --- 320,351 ---- extern int delete_after; extern int orig_umask; struct stats initial_stats; + pid_t pid = 0; /* assignment to get rid of compiler warning */ + int status; + extern char *dest_filter; + #define MAX_FILTER_ARGS 100 + char *filter_argv[MAX_FILTER_ARGS + 1]; if (verbose > 2) { rprintf(FINFO,"recv_files(%d) starting\n",flist->count); } + if (dest_filter) { + char *p; + char *sep = " \t"; + int i; + for (p = strtok(dest_filter, sep), i = 0; + p && i < MAX_FILTER_ARGS; + p = strtok(0, sep)) { + filter_argv[i++] = p; + } + filter_argv[i] = 0; + if (p) { + rprintf(FERROR,"Too many arguments to dest-filter (> %d)\n", i); + exit_cleanup(RERR_SYNTAX); + } + } + while (1) { cleanup_disable(); *************** *** 448,463 **** log_transfer(file, fname); } /* recv file data */ recv_ok = receive_data(f_in,buf,fd2,fname,file->length); log_recv(file, &initial_stats); if (buf) unmap_file(buf); if (fd1 != -1) { close(fd1); } ! close(fd2); if (verbose > 2) rprintf(FINFO,"renaming %s to %s\n",fnametmp,fname); --- 469,502 ---- log_transfer(file, fname); } + if (dest_filter) { + pid = run_filter(filter_argv, fd2, &fd2); + } + /* recv file data */ recv_ok = receive_data(f_in,buf,fd2,fname,file->length); + if (dest_filter) { + close(fd2); + wait_process(pid, &status); + if (status != 0) { + rprintf(FERROR,"filter %s exited code: %d\n", + dest_filter, status); + if (buf) unmap_file(buf); + if (fd1 != -1) close(fd1); + continue; + } + } + log_recv(file, &initial_stats); if (buf) unmap_file(buf); if (fd1 != -1) { close(fd1); } ! if (! dest_filter) { ! close(fd2); ! } if (verbose > 2) rprintf(FINFO,"renaming %s to %s\n",fnametmp,fname); *** rsync-2.5.6/rsync.1 Mon Jan 27 19:11:57 2003 --- rsync-2.5.6.patched/rsync.1 Tue Apr 1 22:20:55 2003 *************** *** 322,327 **** --- 322,328 ---- -v, --verbose increase verbosity -q, --quiet decrease verbosity -c, --checksum always checksum + --dest-filter=COMMAND filter file through COMMAND at destination -a, --archive archive mode, equivalent to -rlptgoD -r, --recursive recurse into directories -R, --relative use relative path names *************** *** 361,366 **** --- 362,368 ---- --timeout=TIME set IO timeout in seconds -I, --ignore-times don\'t exclude files that match length and time --size-only only use file size when determining if a file should be transferred + --times-only only use file modification time when determining if a file should be transferred --modify-window=NUM Timestamp window (seconds) for file match (default=0) -T --temp-dir=DIR create temporary files in directory DIR --compare-dest=DIR also compare destination files relative to DIR *************** *** 437,442 **** --- 439,450 ---- after using another mirroring system which may not preserve timestamps exactly\&. .IP + .IP "\fB--times-only\fP" + With this option, rsync will ignore size and file content + differences when deciding whether to transfer a file\&. Only + a difference in file modification time will cause a file to be + transferred. + .IP .IP "\fB--modify-window\fP" When comparing two timestamps rsync treats the timestamps as being equal if they are within the value of *************** *** 451,456 **** --- 459,477 ---- explicitly checked on the receiver and any files of the same name which already exist and have the same checksum and size on the receiver are skipped\&. This option can be quite slow\&. + .IP + .IP "\fB --dest-filter=COMMAND\fP" + This option allows you to specify a filter program that will be + applied to the contents of all transferred regular files before + the data is written to disk. COMMAND will receive the data on its + standard input and it should write the filtered data to standard + output. COMMAND should exit non-zero if it cannot process the + data or if it encounters an error when writing the data to stdout. + Example: --dest-filter="gzip -9" will cause remote files to be compressed. + Use of --dest-filter automatically enables --whole-file. + If your filter does not output the same number of bytes that it + received on input, you should use --times-only to disable size and + content checks on subsequent rsync runs. .IP .IP "\fB-a, --archive\fP" This is equivalent to -rlptgoD\&. It is a quick *** rsync-2.5.6/util.c Sun Jan 19 13:37:11 2003 --- rsync-2.5.6.patched/util.c Tue Apr 1 23:51:02 2003 *************** *** 381,389 **** --- 381,393 ---- { pid_t newpid = fork(); + /* --dest-filter can launch thousands of processes. + This is too clumsy to deal with it. */ + #if 0 if (newpid != 0 && newpid != -1) { all_pids[num_pids++] = newpid; } + #endif return newpid; } -- To unsubscribe or change options: http://lists.samba.org/mailman/listinfo/rsync Before posting, read: http://www.tuxedo.org/~esr/faqs/smart-questions.html