Annotation of embedaddon/rsync/patches/source-filter_dest-filter.diff, revision 1.1.1.1

1.1       misho       1: CAUTION:  This patch compiles, but is otherwise totally untested!
                      2: 
                      3: This patch also implements --times-only.
                      4: 
                      5: Implementation details for the --source-filter and -dest-filter options:
                      6: 
                      7:  - These options open a *HUGE* security hole in daemon mode unless they
                      8:    are refused in your rsyncd.conf!
                      9: 
                     10:  - Filtering disables rsync alogrithm. (This should be fixed.)
                     11: 
                     12:  - Source filter makes temporary files in /tmp. (Should be overridable.)
                     13: 
                     14:  - If source filter fails, data is send unfiltered. (Should be changed
                     15:    to abort.)
                     16: 
                     17:  - Failure of destination filter, causes data loss!!! (Should be changed
                     18:    to abort.)
                     19: 
                     20:  - If filter changes size of file, you should use --times-only option to
                     21:    prevent repeated transfers of unchanged files.
                     22: 
                     23:  - If the COMMAND contains single quotes, option-passing breaks.  (Needs
                     24:    to be fixed.)
                     25: 
                     26: To use this patch, run these commands for a successful build:
                     27: 
                     28:     patch -p1 <patches/source-filter_dest-filter.diff
                     29:     ./prepare-source
                     30:     ./configure                                (optional if already run)
                     31:     make
                     32: 
                     33: based-on: e94bad1c156fc3910f24e2b3b71a81b0b0bdeb70
                     34: diff --git a/generator.c b/generator.c
                     35: --- a/generator.c
                     36: +++ b/generator.c
                     37: @@ -65,6 +65,7 @@ extern int append_mode;
                     38:  extern int make_backups;
                     39:  extern int csum_length;
                     40:  extern int ignore_times;
                     41: +extern int times_only;
                     42:  extern int size_only;
                     43:  extern OFF_T max_size;
                     44:  extern OFF_T min_size;
                     45: @@ -603,7 +604,7 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
                     46:  /* Perform our quick-check heuristic for determining if a file is unchanged. */
                     47:  int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st)
                     48:  {
                     49: -      if (st->st_size != F_LENGTH(file))
                     50: +      if (!times_only && st->st_size != F_LENGTH(file))
                     51:                return 0;
                     52:  
                     53:        /* if always checksum is set then we use the checksum instead
                     54: diff --git a/main.c b/main.c
                     55: --- a/main.c
                     56: +++ b/main.c
                     57: @@ -187,7 +187,7 @@ int shell_exec(const char *cmd)
                     58:  }
                     59:  
                     60:  /* Wait for a process to exit, calling io_flush while waiting. */
                     61: -static void wait_process_with_flush(pid_t pid, int *exit_code_ptr)
                     62: +void wait_process_with_flush(pid_t pid, int *exit_code_ptr)
                     63:  {
                     64:        pid_t waited_pid;
                     65:        int status;
                     66: diff --git a/options.c b/options.c
                     67: --- a/options.c
                     68: +++ b/options.c
                     69: @@ -110,6 +110,7 @@ int safe_symlinks = 0;
                     70:  int copy_unsafe_links = 0;
                     71:  int munge_symlinks = 0;
                     72:  int size_only = 0;
                     73: +int times_only = 0;
                     74:  int daemon_bwlimit = 0;
                     75:  int bwlimit = 0;
                     76:  int fuzzy_basis = 0;
                     77: @@ -170,6 +171,8 @@ char *logfile_name = NULL;
                     78:  char *logfile_format = NULL;
                     79:  char *stdout_format = NULL;
                     80:  char *password_file = NULL;
                     81: +char *source_filter = NULL;
                     82: +char *dest_filter = NULL;
                     83:  char *early_input_file = NULL;
                     84:  char *rsync_path = RSYNC_PATH;
                     85:  char *backup_dir = NULL;
                     86: @@ -679,6 +682,7 @@ static struct poptOption long_options[] = {
                     87:    {"chmod",            0,  POPT_ARG_STRING, 0, OPT_CHMOD, 0, 0 },
                     88:    {"ignore-times",    'I', POPT_ARG_NONE,   &ignore_times, 0, 0, 0 },
                     89:    {"size-only",        0,  POPT_ARG_NONE,   &size_only, 0, 0, 0 },
                     90: +  {"times-only",       0,  POPT_ARG_NONE,   &times_only , 0, 0, 0 },
                     91:    {"one-file-system", 'x', POPT_ARG_NONE,   0, 'x', 0, 0 },
                     92:    {"no-one-file-system",0, POPT_ARG_VAL,    &one_file_system, 0, 0, 0 },
                     93:    {"no-x",             0,  POPT_ARG_VAL,    &one_file_system, 0, 0, 0 },
                     94: @@ -813,6 +817,8 @@ static struct poptOption long_options[] = {
                     95:    {"early-input",      0,  POPT_ARG_STRING, &early_input_file, 0, 0, 0 },
                     96:    {"blocking-io",      0,  POPT_ARG_VAL,    &blocking_io, 1, 0, 0 },
                     97:    {"no-blocking-io",   0,  POPT_ARG_VAL,    &blocking_io, 0, 0, 0 },
                     98: +  {"source-filter",    0,  POPT_ARG_STRING, &source_filter, 0, 0, 0 },
                     99: +  {"dest-filter",      0,  POPT_ARG_STRING, &dest_filter, 0, 0, 0 },
                    100:    {"outbuf",           0,  POPT_ARG_STRING, &outbuf_mode, 0, 0, 0 },
                    101:    {"remote-option",   'M', POPT_ARG_STRING, 0, 'M', 0, 0 },
                    102:    {"protocol",         0,  POPT_ARG_INT,    &protocol_version, 0, 0, 0 },
                    103: @@ -2390,6 +2396,16 @@ int parse_arguments(int *argc_p, const char ***argv_p)
                    104:                }
                    105:        }
                    106:  
                    107: +      if (source_filter || dest_filter) {
                    108: +              if (whole_file == 0) {
                    109: +                      snprintf(err_buf, sizeof err_buf,
                    110: +                               "--no-whole-file cannot be used with --%s-filter\n",
                    111: +                               source_filter ? "source" : "dest");
                    112: +                      return 0;
                    113: +              }
                    114: +              whole_file = 1;
                    115: +      }
                    116: +
                    117:        if (files_from) {
                    118:                char *h, *p;
                    119:                int q;
                    120: @@ -2782,6 +2798,25 @@ void server_options(char **args, int *argc_p)
                    121:        else if (missing_args == 1 && !am_sender)
                    122:                args[ac++] = "--ignore-missing-args";
                    123:  
                    124: +      if (times_only && am_sender)
                    125: +              args[ac++] = "--times-only";
                    126: +
                    127: +      if (source_filter && !am_sender) {
                    128: +              /* Need to single quote the arg to keep the remote shell
                    129: +               * from splitting it.  FIXME: breaks if command has single quotes. */
                    130: +              if (asprintf(&arg, "--source-filter='%s'", source_filter) < 0)
                    131: +                      goto oom;
                    132: +              args[ac++] = arg;
                    133: +      }
                    134: +
                    135: +      if (dest_filter && am_sender) {
                    136: +              /* Need to single quote the arg to keep the remote shell
                    137: +               * from splitting it.  FIXME: breaks if command has single quotes. */
                    138: +              if (asprintf(&arg, "--dest-filter='%s'", dest_filter) < 0)
                    139: +                      goto oom;
                    140: +              args[ac++] = arg;
                    141: +      }
                    142: +
                    143:        if (modify_window_set && am_sender) {
                    144:                char *fmt = modify_window < 0 ? "-@%d" : "--modify-window=%d";
                    145:                if (asprintf(&arg, fmt, modify_window) < 0)
                    146: diff --git a/pipe.c b/pipe.c
                    147: --- a/pipe.c
                    148: +++ b/pipe.c
                    149: @@ -27,6 +27,7 @@ extern int am_server;
                    150:  extern int blocking_io;
                    151:  extern int filesfrom_fd;
                    152:  extern int munge_symlinks;
                    153: +extern mode_t orig_umask;
                    154:  extern char *logfile_name;
                    155:  extern int remote_option_cnt;
                    156:  extern const char **remote_options;
                    157: @@ -176,3 +177,77 @@ pid_t local_child(int argc, char **argv, int *f_in, int *f_out,
                    158:  
                    159:        return pid;
                    160:  }
                    161: +
                    162: +pid_t run_filter(char *command[], int out, int *pipe_to_filter)
                    163: +{
                    164: +      pid_t pid;
                    165: +      int pipefds[2];
                    166: +
                    167: +      if (DEBUG_GTE(CMD, 1))
                    168: +              print_child_argv("opening connection using:", command);
                    169: +
                    170: +      if (pipe(pipefds) < 0) {
                    171: +              rsyserr(FERROR, errno, "pipe");
                    172: +              exit_cleanup(RERR_IPC);
                    173: +      }
                    174: +
                    175: +      pid = do_fork();
                    176: +      if (pid == -1) {
                    177: +              rsyserr(FERROR, errno, "fork");
                    178: +              exit_cleanup(RERR_IPC);
                    179: +      }
                    180: +
                    181: +      if (pid == 0) {
                    182: +              if (dup2(pipefds[0], STDIN_FILENO) < 0
                    183: +               || close(pipefds[1]) < 0
                    184: +               || dup2(out, STDOUT_FILENO) < 0) {
                    185: +                      rsyserr(FERROR, errno, "Failed dup/close");
                    186: +                      exit_cleanup(RERR_IPC);
                    187: +              }
                    188: +              umask(orig_umask);
                    189: +              set_blocking(STDIN_FILENO);
                    190: +              if (blocking_io)
                    191: +                      set_blocking(STDOUT_FILENO);
                    192: +              execvp(command[0], command);
                    193: +              rsyserr(FERROR, errno, "Failed to exec %s", command[0]);
                    194: +              exit_cleanup(RERR_IPC);
                    195: +      }
                    196: +
                    197: +      if (close(pipefds[0]) < 0) {
                    198: +              rsyserr(FERROR, errno, "Failed to close");
                    199: +              exit_cleanup(RERR_IPC);
                    200: +      }
                    201: +
                    202: +      *pipe_to_filter = pipefds[1];
                    203: +
                    204: +      return pid;
                    205: +}
                    206: +
                    207: +pid_t run_filter_on_file(char *command[], int out, int in)
                    208: +{
                    209: +      pid_t pid;
                    210: +
                    211: +      if (DEBUG_GTE(CMD, 1))
                    212: +              print_child_argv("opening connection using:", command);
                    213: +
                    214: +      pid = do_fork();
                    215: +      if (pid == -1) {
                    216: +              rsyserr(FERROR, errno, "fork");
                    217: +              exit_cleanup(RERR_IPC);
                    218: +      }
                    219: +
                    220: +      if (pid == 0) {
                    221: +              if (dup2(in, STDIN_FILENO) < 0
                    222: +               || dup2(out, STDOUT_FILENO) < 0) {
                    223: +                      rsyserr(FERROR, errno, "Failed to dup2");
                    224: +                      exit_cleanup(RERR_IPC);
                    225: +              }
                    226: +              if (blocking_io)
                    227: +                      set_blocking(STDOUT_FILENO);
                    228: +              execvp(command[0], command);
                    229: +              rsyserr(FERROR, errno, "Failed to exec %s", command[0]);
                    230: +              exit_cleanup(RERR_IPC);
                    231: +      }
                    232: +
                    233: +      return pid;
                    234: +}
                    235: diff --git a/receiver.c b/receiver.c
                    236: --- a/receiver.c
                    237: +++ b/receiver.c
                    238: @@ -60,6 +60,7 @@ extern BOOL want_progress_now;
                    239:  extern mode_t orig_umask;
                    240:  extern struct stats stats;
                    241:  extern char *tmpdir;
                    242: +extern char *dest_filter;
                    243:  extern char *partial_dir;
                    244:  extern char *basis_dir[MAX_BASIS_DIRS+1];
                    245:  extern char sender_file_sum[MAX_DIGEST_LEN];
                    246: @@ -522,6 +523,7 @@ int recv_files(int f_in, int f_out, char *local_name)
                    247:        char *fnametmp, fnametmpbuf[MAXPATHLEN];
                    248:        char *fnamecmp, *partialptr;
                    249:        char fnamecmpbuf[MAXPATHLEN];
                    250: +      char *filter_argv[MAX_FILTER_ARGS + 1];
                    251:        uchar fnamecmp_type;
                    252:        struct file_struct *file;
                    253:        int itemizing = am_server ? logfile_format_has_i : stdout_format_has_i;
                    254: @@ -532,6 +534,7 @@ int recv_files(int f_in, int f_out, char *local_name)
                    255:        const char *parent_dirname = "";
                    256:  #endif
                    257:        int ndx, recv_ok, one_inplace;
                    258: +      pid_t pid = 0;
                    259:  
                    260:        if (DEBUG_GTE(RECV, 1))
                    261:                rprintf(FINFO, "recv_files(%d) starting\n", cur_flist->used);
                    262: @@ -539,6 +542,23 @@ int recv_files(int f_in, int f_out, char *local_name)
                    263:        if (delay_updates)
                    264:                delayed_bits = bitbag_create(cur_flist->used + 1);
                    265:  
                    266: +      if (dest_filter) {
                    267: +              char *p;
                    268: +              char *sep = " \t";
                    269: +              int i;
                    270: +              for (p = strtok(dest_filter, sep), i = 0;
                    271: +                   p && i < MAX_FILTER_ARGS;
                    272: +                   p = strtok(0, sep))
                    273: +                      filter_argv[i++] = p;
                    274: +              filter_argv[i] = NULL;
                    275: +              if (p) {
                    276: +                      rprintf(FERROR,
                    277: +                              "Too many arguments to dest-filter (> %d)\n",
                    278: +                              MAX_FILTER_ARGS);
                    279: +                      exit_cleanup(RERR_SYNTAX);
                    280: +              }
                    281: +      }
                    282: +
                    283:        progress_init();
                    284:  
                    285:        while (1) {
                    286: @@ -853,6 +873,9 @@ int recv_files(int f_in, int f_out, char *local_name)
                    287:                else if (!am_server && INFO_GTE(NAME, 1) && INFO_EQ(PROGRESS, 1))
                    288:                        rprintf(FINFO, "%s\n", fname);
                    289:  
                    290: +              if (dest_filter)
                    291: +                      pid = run_filter(filter_argv, fd2, &fd2);
                    292: +
                    293:                /* recv file data */
                    294:                recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size, fname, fd2, file, inplace || one_inplace);
                    295:  
                    296: @@ -868,6 +891,16 @@ int recv_files(int f_in, int f_out, char *local_name)
                    297:                        exit_cleanup(RERR_FILEIO);
                    298:                }
                    299:  
                    300: +              if (dest_filter) {
                    301: +                      int status;
                    302: +                      wait_process_with_flush(pid, &status);
                    303: +                      if (status != 0) {
                    304: +                              rprintf(FERROR, "filter %s exited code: %d\n",
                    305: +                                      dest_filter, status);
                    306: +                              continue;
                    307: +                      }
                    308: +              }
                    309: +
                    310:                if ((recv_ok && (!delay_updates || !partialptr)) || inplace) {
                    311:                        if (partialptr == fname)
                    312:                                partialptr = NULL;
                    313: diff --git a/rsync.1.md b/rsync.1.md
                    314: --- a/rsync.1.md
                    315: +++ b/rsync.1.md
                    316: @@ -418,6 +418,7 @@ detailed description below for a complete description.
                    317:  --contimeout=SECONDS     set daemon connection timeout in seconds
                    318:  --ignore-times, -I       don't skip files that match size and time
                    319:  --size-only              skip files that match in size
                    320: +--times-only             skip files that match in mod-time
                    321:  --modify-window=NUM, -@  set the accuracy for mod-time comparisons
                    322:  --temp-dir=DIR, -T       create temporary files in directory DIR
                    323:  --fuzzy, -y              find similar file for basis if no dest file
                    324: @@ -464,6 +465,8 @@ detailed description below for a complete description.
                    325:  --write-batch=FILE       write a batched update to FILE
                    326:  --only-write-batch=FILE  like --write-batch but w/o updating dest
                    327:  --read-batch=FILE        read a batched update from FILE
                    328: +--source-filter=COMMAND  filter file through COMMAND at source
                    329: +--dest-filter=COMMAND    filter file through COMMAND at destination
                    330:  --protocol=NUM           force an older protocol version to be used
                    331:  --iconv=CONVERT_SPEC     request charset conversion of filenames
                    332:  --checksum-seed=NUM      set block/file checksum seed (advanced)
                    333: @@ -3277,6 +3280,36 @@ your home directory (remove the '=' for that).
                    334:      `--write-batch`.  If _FILE_ is `-`, the batch data will be read from
                    335:      standard input. See the "BATCH MODE" section for details.
                    336:  
                    337: +0.  `--source-filter=COMMAND`
                    338: +
                    339: +    This option allows the user to specify a filter program that will be
                    340: +    applied to the contents of all transferred regular files before the data is
                    341: +    sent to destination.  COMMAND will receive the data on its standard input
                    342: +    and it should write the filtered data to standard output.  COMMAND should
                    343: +    exit non-zero if it cannot process the data or if it encounters an error
                    344: +    when writing the data to stdout.
                    345: +
                    346: +    Example: `--source-filter="gzip -9"` will cause remote files to be
                    347: +    compressed.  Use of `--source-filter` automatically enables `--whole-file`.
                    348: +    If your filter does not output the same number of bytes that it received on
                    349: +    input, you should use `--times-only` to disable size and content checks on
                    350: +    subsequent rsync runs.
                    351: +
                    352: +0.  `--dest-filter=COMMAND`
                    353: +
                    354: +    This option allows you to specify a filter program that will be applied to
                    355: +    the contents of all transferred regular files before the data is written to
                    356: +    disk.  COMMAND will receive the data on its standard input and it should
                    357: +    write the filtered data to standard output.  COMMAND should exit non-zero
                    358: +    if it cannot process the data or if it encounters an error when writing the
                    359: +    data to stdout.
                    360: +
                    361: +    Example: --dest-filter="gzip -9" will cause remote files to be compressed.
                    362: +    Use of --dest-filter automatically enables --whole-file.  If your filter
                    363: +    does not output the same number of bytes that it received on input, you
                    364: +    should use --times-only to disable size and content checks on subsequent
                    365: +    rsync runs.
                    366: +
                    367:  0.  `--protocol=NUM`
                    368:  
                    369:      Force an older protocol version to be used.  This is useful for creating a
                    370: diff --git a/rsync.h b/rsync.h
                    371: --- a/rsync.h
                    372: +++ b/rsync.h
                    373: @@ -159,6 +159,7 @@
                    374:  #define IOERR_DEL_LIMIT (1<<2)
                    375:  
                    376:  #define MAX_ARGS 1000
                    377: +#define MAX_FILTER_ARGS 100
                    378:  #define MAX_BASIS_DIRS 20
                    379:  #define MAX_SERVER_ARGS (MAX_BASIS_DIRS*2 + 100)
                    380:  
                    381: diff --git a/sender.c b/sender.c
                    382: --- a/sender.c
                    383: +++ b/sender.c
                    384: @@ -21,6 +21,7 @@
                    385:  
                    386:  #include "rsync.h"
                    387:  #include "inums.h"
                    388: +#include "ifuncs.h"
                    389:  
                    390:  extern int do_xfers;
                    391:  extern int am_server;
                    392: @@ -47,6 +48,7 @@ extern int batch_fd;
                    393:  extern int write_batch;
                    394:  extern int file_old_total;
                    395:  extern BOOL want_progress_now;
                    396: +extern char *source_filter;
                    397:  extern struct stats stats;
                    398:  extern struct file_list *cur_flist, *first_flist, *dir_flist;
                    399:  
                    400: @@ -200,6 +202,26 @@ void send_files(int f_in, int f_out)
                    401:        int f_xfer = write_batch < 0 ? batch_fd : f_out;
                    402:        int save_io_error = io_error;
                    403:        int ndx, j;
                    404: +      char *filter_argv[MAX_FILTER_ARGS + 1];
                    405: +      char *tmp = 0;
                    406: +      int unlink_tmp = 0;
                    407: +
                    408: +      if (source_filter) {
                    409: +              char *p;
                    410: +              char *sep = " \t";
                    411: +              int i;
                    412: +              for (p = strtok(source_filter, sep), i = 0;
                    413: +                   p && i < MAX_FILTER_ARGS;
                    414: +                   p = strtok(0, sep))
                    415: +                      filter_argv[i++] = p;
                    416: +              filter_argv[i] = NULL;
                    417: +              if (p) {
                    418: +                      rprintf(FERROR,
                    419: +                              "Too many arguments to source-filter (> %d)\n",
                    420: +                              MAX_FILTER_ARGS);
                    421: +                      exit_cleanup(RERR_SYNTAX);
                    422: +              }
                    423: +      }
                    424:  
                    425:        if (DEBUG_GTE(SEND, 1))
                    426:                rprintf(FINFO, "send_files starting\n");
                    427: @@ -334,6 +356,7 @@ void send_files(int f_in, int f_out)
                    428:                        exit_cleanup(RERR_PROTOCOL);
                    429:                }
                    430:  
                    431: +              unlink_tmp = 0;
                    432:                fd = do_open(fname, O_RDONLY, 0);
                    433:                if (fd == -1) {
                    434:                        if (errno == ENOENT) {
                    435: @@ -353,6 +376,33 @@ void send_files(int f_in, int f_out)
                    436:                        continue;
                    437:                }
                    438:  
                    439: +              if (source_filter) {
                    440: +                      int fd2;
                    441: +                      char *tmpl = "/tmp/rsync-filtered_sourceXXXXXX";
                    442: +
                    443: +                      tmp = strdup(tmpl);
                    444: +                      fd2 = mkstemp(tmp);
                    445: +                      if (fd2 == -1) {
                    446: +                              rprintf(FERROR, "mkstemp %s failed: %s\n",
                    447: +                                      tmp, strerror(errno));
                    448: +                      } else {
                    449: +                              int status;
                    450: +                              pid_t pid = run_filter_on_file(filter_argv, fd2, fd);
                    451: +                              close(fd);
                    452: +                              close(fd2);
                    453: +                              wait_process_with_flush(pid, &status);
                    454: +                              if (status != 0) {
                    455: +                                      rprintf(FERROR,
                    456: +                                          "bypassing source filter %s; exited with code: %d\n",
                    457: +                                          source_filter, status);
                    458: +                                      fd = do_open(fname, O_RDONLY, 0);
                    459: +                              } else {
                    460: +                                      fd = do_open(tmp, O_RDONLY, 0);
                    461: +                                      unlink_tmp = 1;
                    462: +                              }
                    463: +                      }
                    464: +              }
                    465: +
                    466:                /* map the local file */
                    467:                if (do_fstat(fd, &st) != 0) {
                    468:                        io_error |= IOERR_GENERAL;
                    469: @@ -404,6 +454,8 @@ void send_files(int f_in, int f_out)
                    470:                        }
                    471:                }
                    472:                close(fd);
                    473: +              if (unlink_tmp)
                    474: +                      unlink(tmp);
                    475:  
                    476:                free_sums(s);
                    477:  
                    478: diff -Nurp a/rsync.1 b/rsync.1
                    479: --- a/rsync.1
                    480: +++ b/rsync.1
                    481: @@ -494,6 +494,7 @@ detailed description below for a complet
                    482:  --contimeout=SECONDS     set daemon connection timeout in seconds
                    483:  --ignore-times, -I       don't skip files that match size and time
                    484:  --size-only              skip files that match in size
                    485: +--times-only             skip files that match in mod-time
                    486:  --modify-window=NUM, -@  set the accuracy for mod-time comparisons
                    487:  --temp-dir=DIR, -T       create temporary files in directory DIR
                    488:  --fuzzy, -y              find similar file for basis if no dest file
                    489: @@ -540,6 +541,8 @@ detailed description below for a complet
                    490:  --write-batch=FILE       write a batched update to FILE
                    491:  --only-write-batch=FILE  like --write-batch but w/o updating dest
                    492:  --read-batch=FILE        read a batched update from FILE
                    493: +--source-filter=COMMAND  filter file through COMMAND at source
                    494: +--dest-filter=COMMAND    filter file through COMMAND at destination
                    495:  --protocol=NUM           force an older protocol version to be used
                    496:  --iconv=CONVERT_SPEC     request charset conversion of filenames
                    497:  --checksum-seed=NUM      set block/file checksum seed (advanced)
                    498: @@ -3334,6 +3337,32 @@ into the batch file without having to fl
                    499:  Apply all of the changes stored in FILE, a file previously generated by
                    500:  \fB\-\-write-batch\fP.  If \fIFILE\fP is \fB\-\fP, the batch data will be read from
                    501:  standard input. See the "BATCH MODE" section for details.
                    502: +.IP "\fB\-\-source-filter=COMMAND\fP"
                    503: +This option allows the user to specify a filter program that will be
                    504: +applied to the contents of all transferred regular files before the data is
                    505: +sent to destination.  COMMAND will receive the data on its standard input
                    506: +and it should write the filtered data to standard output.  COMMAND should
                    507: +exit non-zero if it cannot process the data or if it encounters an error
                    508: +when writing the data to stdout.
                    509: +.IP
                    510: +Example: \fB\-\-source-filter="gzip\ \-9"\fP will cause remote files to be
                    511: +compressed.  Use of \fB\-\-source-filter\fP automatically enables \fB\-\-whole-file\fP.
                    512: +If your filter does not output the same number of bytes that it received on
                    513: +input, you should use \fB\-\-times-only\fP to disable size and content checks on
                    514: +subsequent rsync runs.
                    515: +.IP "\fB\-\-dest-filter=COMMAND\fP"
                    516: +This option allows you to specify a filter program that will be applied to
                    517: +the contents of all transferred regular files before the data is written to
                    518: +disk.  COMMAND will receive the data on its standard input and it should
                    519: +write the filtered data to standard output.  COMMAND should exit non-zero
                    520: +if it cannot process the data or if it encounters an error when writing the
                    521: +data to stdout.
                    522: +.IP
                    523: +Example: \-\-dest-filter="gzip \-9" will cause remote files to be compressed.
                    524: +Use of \-\-dest-filter automatically enables \-\-whole-file.  If your filter
                    525: +does not output the same number of bytes that it received on input, you
                    526: +should use \-\-times-only to disable size and content checks on subsequent
                    527: +rsync runs.
                    528:  .IP "\fB\-\-protocol=NUM\fP"
                    529:  Force an older protocol version to be used.  This is useful for creating a
                    530:  batch file that is compatible with an older version of rsync.  For
                    531: diff -Nurp a/rsync.1.html b/rsync.1.html
                    532: --- a/rsync.1.html
                    533: +++ b/rsync.1.html
                    534: @@ -409,6 +409,7 @@ detailed description below for a complet
                    535:  --contimeout=SECONDS     set daemon connection timeout in seconds
                    536:  --ignore-times, -I       don't skip files that match size and time
                    537:  --size-only              skip files that match in size
                    538: +--times-only             skip files that match in mod-time
                    539:  --modify-window=NUM, -@  set the accuracy for mod-time comparisons
                    540:  --temp-dir=DIR, -T       create temporary files in directory DIR
                    541:  --fuzzy, -y              find similar file for basis if no dest file
                    542: @@ -455,6 +456,8 @@ detailed description below for a complet
                    543:  --write-batch=FILE       write a batched update to FILE
                    544:  --only-write-batch=FILE  like --write-batch but w/o updating dest
                    545:  --read-batch=FILE        read a batched update from FILE
                    546: +--source-filter=COMMAND  filter file through COMMAND at source
                    547: +--dest-filter=COMMAND    filter file through COMMAND at destination
                    548:  --protocol=NUM           force an older protocol version to be used
                    549:  --iconv=CONVERT_SPEC     request charset conversion of filenames
                    550:  --checksum-seed=NUM      set block/file checksum seed (advanced)
                    551: @@ -3091,6 +3094,34 @@ into the batch file without having to fl
                    552:  standard input. See the &quot;BATCH MODE&quot; section for details.</p>
                    553:  </dd>
                    554:  
                    555: +<dt><code>--source-filter=COMMAND</code></dt><dd>
                    556: +<p>This option allows the user to specify a filter program that will be
                    557: +applied to the contents of all transferred regular files before the data is
                    558: +sent to destination.  COMMAND will receive the data on its standard input
                    559: +and it should write the filtered data to standard output.  COMMAND should
                    560: +exit non-zero if it cannot process the data or if it encounters an error
                    561: +when writing the data to stdout.</p>
                    562: +<p>Example: <code>--source-filter=&quot;gzip -9&quot;</code> will cause remote files to be
                    563: +compressed.  Use of <code>--source-filter</code> automatically enables <code>--whole-file</code>.
                    564: +If your filter does not output the same number of bytes that it received on
                    565: +input, you should use <code>--times-only</code> to disable size and content checks on
                    566: +subsequent rsync runs.</p>
                    567: +</dd>
                    568: +
                    569: +<dt><code>--dest-filter=COMMAND</code></dt><dd>
                    570: +<p>This option allows you to specify a filter program that will be applied to
                    571: +the contents of all transferred regular files before the data is written to
                    572: +disk.  COMMAND will receive the data on its standard input and it should
                    573: +write the filtered data to standard output.  COMMAND should exit non-zero
                    574: +if it cannot process the data or if it encounters an error when writing the
                    575: +data to stdout.</p>
                    576: +<p>Example: -&#8288;-&#8288;dest-filter=&quot;gzip -&#8288;9&quot; will cause remote files to be compressed.
                    577: +Use of -&#8288;-&#8288;dest-filter automatically enables -&#8288;-&#8288;whole-file.  If your filter
                    578: +does not output the same number of bytes that it received on input, you
                    579: +should use -&#8288;-&#8288;times-only to disable size and content checks on subsequent
                    580: +rsync runs.</p>
                    581: +</dd>
                    582: +
                    583:  <dt><code>--protocol=NUM</code></dt><dd>
                    584:  <p>Force an older protocol version to be used.  This is useful for creating a
                    585:  batch file that is compatible with an older version of rsync.  For

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>