After applying this patch and running configure, you MUST run this command before "make": make proto --- orig/generator.c 2005-02-16 15:35:52 +++ generator.c 2005-02-15 21:33:51 @@ -38,6 +38,7 @@ extern int preserve_gid; extern int preserve_times; extern int omit_dir_times; extern int delete_during; +extern int remove_sent_files; extern int update_only; extern int opt_ignore_existing; extern int inplace; @@ -510,6 +511,11 @@ static void recv_generator(char *fname, rprintf(FINFO, "%s -> %s\n", safe_fname(fname), safe_fname(file->u.link)); } + if (remove_sent_files && !dry_run) { + char numbuf[4]; + SIVAL(numbuf, 0, ndx); + io_multiplex_write(MSG_SUCCESS, numbuf, 4); + } } #endif return; --- orig/io.c 2005-02-03 02:04:20 +++ io.c 2005-02-15 21:33:51 @@ -244,6 +244,14 @@ static void read_msg_fd(void) read_loop(fd, buf, 4); redo_list_add(IVAL(buf,0)); break; + case MSG_SUCCESS: + if (len != 4) { + rprintf(FERROR, "invalid message %d:%d\n", tag, len); + exit_cleanup(RERR_STREAMIO); + } + read_loop(fd, buf, len); + io_multiplex_write(MSG_SUCCESS, buf, len); + break; case MSG_INFO: case MSG_ERROR: case MSG_LOG: @@ -677,6 +685,16 @@ static int readfd_unbuffered(int fd, cha read_loop(fd, iobuf_in, remaining); iobuf_in_ndx = 0; break; + case MSG_SUCCESS: + if (remaining != 4) { + rprintf(FERROR, "invalid multi-message %d:%ld\n", + tag, (long)remaining); + exit_cleanup(RERR_STREAMIO); + } + read_loop(fd, line, remaining); + successful_send(IVAL(line, 0)); + remaining = 0; + break; case MSG_INFO: case MSG_ERROR: if (remaining >= sizeof line) { --- orig/main.c 2005-02-15 19:27:04 +++ main.c 2005-02-15 21:33:51 @@ -33,12 +33,14 @@ extern int verbose; extern int itemize_changes; extern int blocking_io; extern int delete_before; +extern int remove_sent_files; extern int daemon_over_rsh; extern int do_stats; extern int dry_run; extern int list_only; extern int log_got_error; extern int module_id; +extern int need_messages_from_generator; extern int orig_umask; extern int copy_links; extern int keep_dirlinks; @@ -442,6 +444,12 @@ static void do_server_sender(int f_in, i exit_cleanup(RERR_SYNTAX); return; } + if (am_daemon && lp_read_only(module_id) && remove_sent_files) { + rprintf(FERROR, + "ERROR: --remove-sent-files cannot be used with a read-only module\n"); + exit_cleanup(RERR_SYNTAX); + return; + } if (!relative_paths && !push_dir(dir)) { rsyserr(FERROR, errno, "push_dir#3 %s failed", @@ -673,6 +681,8 @@ void start_server(int f_in, int f_out, i if (am_sender) { keep_dirlinks = 0; /* Must be disabled on the sender. */ + if (need_messages_from_generator) + io_start_multiplex_in(); recv_filter_list(f_in); do_server_sender(f_in, f_out, argc, argv); @@ -750,6 +760,9 @@ int client_run(int f_in, int f_out, pid_ exit_cleanup(status); } + if (need_messages_from_generator && !read_batch) + io_start_multiplex_out(); + if (argc == 0) list_only |= 1; --- orig/options.c 2005-02-16 15:35:53 +++ options.c 2005-02-15 21:33:52 @@ -59,6 +59,7 @@ int delete_during = 0; int delete_before = 0; int delete_after = 0; int delete_excluded = 0; +int remove_sent_files = 0; int one_file_system = 0; int protocol_version = PROTOCOL_VERSION; int sparse_files = 0; @@ -93,6 +94,7 @@ int fuzzy_basis = 0; size_t bwlimit_writemax = 0; int only_existing = 0; int opt_ignore_existing = 0; +int need_messages_from_generator = 0; int max_delete = 0; OFF_T max_size = 0; int ignore_errors = 0; @@ -286,6 +288,7 @@ void usage(enum logcode F) rprintf(F," --rsync-path=PATH specify path to rsync on the remote machine\n"); rprintf(F," --existing only update files that already exist on receiver\n"); rprintf(F," --ignore-existing ignore files that already exist on receiving side\n"); + rprintf(F," --remove-sent-files sent files/symlinks are removed from sending side\n"); rprintf(F," --del an alias for --delete-during\n"); rprintf(F," --delete delete files that don't exist on the sending side\n"); rprintf(F," --delete-before receiver deletes before transfer (default)\n"); @@ -369,6 +372,7 @@ static struct poptOption long_options[] {"delete-during", 0, POPT_ARG_NONE, &delete_during, 0, 0, 0 }, {"delete-after", 0, POPT_ARG_NONE, &delete_after, 0, 0, 0 }, {"delete-excluded", 0, POPT_ARG_NONE, &delete_excluded, 0, 0, 0 }, + {"remove-sent-files",0, POPT_ARG_NONE, &remove_sent_files, 0, 0, 0 }, {"force", 0, POPT_ARG_NONE, &force_delete, 0, 0, 0 }, {"numeric-ids", 0, POPT_ARG_NONE, &numeric_ids, 0, 0, 0 }, {"filter", 'f', POPT_ARG_STRING, 0, OPT_FILTER, 0, 0 }, @@ -969,6 +973,17 @@ int parse_arguments(int *argc, const cha return 0; } + if (remove_sent_files) { + /* We only want to infer this refusal of --remove-sent-files + * via the refusal of "delete", not any of the "delete-FOO" + * options. */ + if (refused_delete && am_sender) { + create_refuse_error(refused_delete); + return 0; + } + need_messages_from_generator = 1; + } + *argv = poptGetArgs(pc); *argc = count_args(*argv); @@ -1411,6 +1426,9 @@ void server_options(char **args,int *arg if (fuzzy_basis && am_sender) args[ac++] = "--fuzzy"; + if (remove_sent_files) + args[ac++] = "--remove-sent-files"; + *argc = ac; return; --- orig/receiver.c 2005-02-16 15:35:53 +++ receiver.c 2005-02-15 21:33:52 @@ -43,6 +43,7 @@ extern int basis_dir_cnt; extern int make_backups; extern int do_progress; extern int cleanup_got_literal; +extern int remove_sent_files; extern int module_id; extern int ignore_errors; extern int orig_umask; @@ -310,7 +311,7 @@ int recv_files(int f_in, struct file_lis char *fname, fbuf[MAXPATHLEN]; char template[MAXPATHLEN]; char fnametmp[MAXPATHLEN]; - char *fnamecmp, *partialptr; + char *fnamecmp, *partialptr, numbuf[4]; char fnamecmpbuf[MAXPATHLEN]; uchar *delayed_bits = NULL; struct file_struct *file; @@ -588,7 +589,12 @@ int recv_files(int f_in, struct file_lis cleanup_disable(); - if (!recv_ok) { + if (recv_ok) { + if (remove_sent_files) { + SIVAL(numbuf, 0, i); + send_msg(MSG_SUCCESS, numbuf, 4); + } + } else { int msgtype = csum_length == SUM_LENGTH || read_batch ? FERROR : FINFO; if (msgtype == FERROR || verbose) { @@ -612,9 +618,8 @@ int recv_files(int f_in, struct file_lis keptstr, redostr); } if (csum_length != SUM_LENGTH) { - char buf[4]; - SIVAL(buf, 0, i); - send_msg(MSG_REDO, buf, 4); + SIVAL(numbuf, 0, i); + send_msg(MSG_REDO, numbuf, 4); } } } --- orig/rsync.yo 2005-02-16 15:35:54 +++ rsync.yo 2005-02-15 21:33:55 @@ -332,6 +332,7 @@ to the detailed description below for a --rsync-path=PATH specify path to rsync on the remote machine --existing only update files that already exist --ignore-existing ignore files that already exist on receiver + --remove-sent-files sent files/symlinks are removed from sender --del an alias for --delete-during --delete delete files that don't exist on sender --delete-before receiver deletes before transfer (default) @@ -665,6 +666,11 @@ dit(bf(--ignore-existing)) This tells rsync not to update files that already exist on the destination. +dit(bf(--remove-sent-files)) This tells rsync to remove from the sending +side the files and/or symlinks that are newly created or whose content is +updated on the receiving side. Directories and devices are not removed, +nor are files/symlinks whose attributes are merely changed. + dit(bf(--delete)) This tells rsync to delete extraneous files from the receiving side (ones that aren't on the sending side), but only for the directories that are being synchronized. You must have asked rsync to --- orig/rsyncd.conf.yo 2005-02-16 15:35:54 +++ rsyncd.conf.yo 2005-02-15 21:33:55 @@ -443,6 +443,10 @@ quote(tt( refuse options = c delete)) The reason the above refuses all delete options is that the options imply bf(--delete), and implied options are refused just like explicit options. +As an additional safety feature, the refusal of "delete" also refuses +bf(remove-sent-files) when the daemon is the sender; if you want the latter +without the former, instead refuse "delete-*" -- that refuses all the +delete modes without affecting bf(--remove-sent-files). When an option is refused, the server prints an error message and exits. To prevent all compression, you can use "dont compress = *" (see below) --- orig/sender.c 2005-02-16 15:35:54 +++ sender.c 2005-02-15 21:33:55 @@ -28,6 +28,7 @@ extern int io_error; extern int dry_run; extern int am_server; extern int am_daemon; +extern int remove_sent_files; extern int protocol_version; extern int updating_basis_file; extern int make_backups; @@ -98,7 +99,32 @@ static struct sum_struct *receive_sums(i return s; } +static struct file_list *the_flist; +void successful_send(int i) +{ + char fname[MAXPATHLEN]; + struct file_struct *file; + unsigned int offset; + + if (!the_flist || i < 0 || i >= the_flist->count) + return; + + file = the_flist->files[i]; + /* The generator might tell us about symlinks we didn't send. */ + if (!(file->flags & FLAG_SENT) && !S_ISLNK(file->mode)) + return; + if (file->dir.root) { + offset = stringjoin(fname, sizeof fname, + file->dir.root, "/", NULL); + } else + offset = 0; + f_name_to(file, fname + offset); + if (remove_sent_files && do_unlink(fname) == 0 && verbose) { + rprintf(FINFO, "sender removed %s\n", + safe_fname(fname + offset)); + } +} void send_files(struct file_list *flist, int f_out, int f_in) { @@ -117,6 +143,8 @@ void send_files(struct file_list *flist, if (verbose > 2) rprintf(FINFO, "send_files starting\n"); + the_flist = flist; + while (1) { unsigned int offset; @@ -281,6 +309,9 @@ void send_files(struct file_list *flist, rprintf(FINFO, "sender finished %s\n", safe_fname(fname)); } + + /* Flag that we actually sent this entry. */ + file->flags |= FLAG_SENT; } make_backups = save_make_backups;