]> git.ipfire.org Git - thirdparty/rsync.git/commitdiff
Add support for negotiated checksum names.
authorWayne Davison <wayne@opencoder.net>
Sun, 24 May 2020 19:56:00 +0000 (12:56 -0700)
committerWayne Davison <wayne@opencoder.net>
Sun, 24 May 2020 20:22:19 +0000 (13:22 -0700)
checksum.c
compat.c
io.c
options.c
rsync.h

index 1be17522e93a1758263ed50cf819feae842ad7a1..60e0b55f3281a35b3c10342502acee35d9002ffc 100644 (file)
@@ -52,26 +52,19 @@ extern char *checksum_choice;
 #define CSUM_MD5 5
 #define CSUM_XXH64 6
 
-#define CSUM_COUNT 7
-
-struct csum_struct {
-       int num;
-       const char *name, *main_name;
-} valid_checksums[] = {
+struct name_num_obj valid_checksums = {
+       "checksum", NULL, NULL, 0, 0, {
 #ifdef SUPPORT_XXHASH
-       { CSUM_XXH64, "xxh64", NULL },
-       { CSUM_XXH64, "xxhash", NULL },
+               { CSUM_XXH64, "xxh64", NULL },
+               { CSUM_XXH64, "xxhash", NULL },
 #endif
-       { CSUM_MD5, "md5", NULL },
-       { CSUM_MD4, "md4", NULL },
-       { CSUM_NONE, "none", NULL },
-       { 0, NULL, NULL }
+               { CSUM_MD5, "md5", NULL },
+               { CSUM_MD4, "md4", NULL },
+               { CSUM_NONE, "none", "" }, /* The "" prevents us from listing this name by default */
+               { 0, NULL, NULL }
+       }
 };
 
-struct csum_struct auto_cs = { 0, "auto", NULL };
-
-#define MAX_CHECKSUM_LIST 1024
-
 #ifndef USE_OPENSSL
 #define MD5_CTX md_context
 #define MD5_Init md5_begin
@@ -81,74 +74,59 @@ struct csum_struct auto_cs = { 0, "auto", NULL };
 
 int xfersum_type = 0; /* used for the file transfer checksums */
 int checksum_type = 0; /* used for the pre-transfer (--checksum) checksums */
-const char *negotiated_csum_name = NULL;
 
-static struct csum_struct *parse_csum_name(const char *name, int len, int allow_auto)
+static int parse_csum_name(const char *name, int len)
 {
-       struct csum_struct *cs;
+       struct name_num_item *nni;
 
        if (len < 0 && name)
                len = strlen(name);
 
-       if (!name || (allow_auto && len == 4 && strncasecmp(name, "auto", 4) == 0)) {
-               cs = &auto_cs;
+       if (!name || (len == 4 && strncasecmp(name, "auto", 4) == 0)) {
                if (protocol_version >= 30)
-                       cs->num = CSUM_MD5;
-               else if (protocol_version >= 27)
-                       cs->num = CSUM_MD4_OLD;
-               else if (protocol_version >= 21)
-                       cs->num = CSUM_MD4_BUSTED;
-               else
-                       cs->num = CSUM_MD4_ARCHAIC;
-               return cs;
+                       return CSUM_MD5;
+               if (protocol_version >= 27)
+                       return CSUM_MD4_OLD;
+               if (protocol_version >= 21)
+                       return CSUM_MD4_BUSTED;
+               return CSUM_MD4_ARCHAIC;
        }
 
-       for (cs = valid_checksums; cs->name; cs++) {
-               if (strncasecmp(name, cs->name, len) == 0 && cs->name[len] == '\0')
-                       return cs;
-       }
+       nni = get_nni_by_name(&valid_checksums, name, len);
 
-       if (allow_auto) {
+       if (!nni) {
                rprintf(FERROR, "unknown checksum name: %s\n", name);
                exit_cleanup(RERR_UNSUPPORTED);
        }
 
-       return NULL;
+       return nni->num;
 }
 
 static const char *checksum_name(int num)
 {
-       struct csum_struct *cs;
-
-       for (cs = valid_checksums; cs->name; cs++) {
-               if (num == cs->num)
-                       return cs->name;
-       }
+       struct name_num_item *nni = get_nni_by_num(&valid_checksums, num);
 
-       if (num < CSUM_MD4)
-               return "MD4";
-
-       return "UNKNOWN"; /* IMPOSSIBLE */
+       return nni ? nni->name : num < CSUM_MD4 ? "MD4" : "UNKNOWN";
 }
 
 void parse_checksum_choice(int final_call)
 {
-       if (!negotiated_csum_name) {
+       if (!valid_checksums.negotiated_name) {
                char *cp = checksum_choice ? strchr(checksum_choice, ',') : NULL;
                if (cp) {
-                       xfersum_type = parse_csum_name(checksum_choice, cp - checksum_choice, 1)->num;
-                       checksum_type = parse_csum_name(cp+1, -1, 1)->num;
+                       xfersum_type = parse_csum_name(checksum_choice, cp - checksum_choice);
+                       checksum_type = parse_csum_name(cp+1, -1);
                } else
-                       xfersum_type = checksum_type = parse_csum_name(checksum_choice, -1, 1)->num;
+                       xfersum_type = checksum_type = parse_csum_name(checksum_choice, -1);
        }
 
        if (xfersum_type == CSUM_NONE)
                whole_file = 1;
 
-       if (final_call && DEBUG_GTE(CSUM, am_server ? 2 : 1)) {
+       if (final_call && DEBUG_GTE(NSTR, am_server ? 2 : 1)) {
                const char *c_s = am_server ? "Server" : "Client";
-               if (negotiated_csum_name)
-                       rprintf(FINFO, "%s negotiated checksum: %s\n", c_s, negotiated_csum_name);
+               if (valid_checksums.negotiated_name)
+                       rprintf(FINFO, "%s negotiated checksum: %s\n", c_s, valid_checksums.negotiated_name);
                else if (xfersum_type == checksum_type) {
                        rprintf(FINFO, "%s %s checksum: %s\n", c_s,
                                checksum_choice ? "chosen" : "protocol-based",
@@ -162,128 +140,6 @@ void parse_checksum_choice(int final_call)
        }
 }
 
-static int parse_checksum_list(const char *from, char *sumbuf, int sumbuf_len, uchar *saw)
-{
-       char *to = sumbuf, *tok = NULL;
-       int cnt = 0;
-
-       while (1) {
-               if (*from == ' ' || !*from) {
-                       if (tok) {
-                               struct csum_struct *cs = parse_csum_name(tok, to - tok, 0);
-                               if (cs && !saw[cs->num]) {
-                                       saw[cs->num] = ++cnt;
-                                       if (cs->main_name) {
-                                               to = tok + strlcpy(tok, cs->main_name, sumbuf_len - (tok - sumbuf));
-                                               if (to - sumbuf >= sumbuf_len) {
-                                                       to = tok - 1;
-                                                       break;
-                                               }
-                                       }
-                               } else
-                                       to = tok - (tok != sumbuf);
-                               tok = NULL;
-                       }
-                       if (!*from++)
-                               break;
-                       continue;
-               }
-               if (!tok) {
-                       if (to != sumbuf)
-                               *to++ = ' ';
-                       tok = to;
-               }
-               if (to - sumbuf >= sumbuf_len - 1) {
-                       to = tok - (tok != sumbuf);
-                       break;
-               }
-               *to++ = *from++;
-       }
-       *to = '\0';
-
-       return to - sumbuf;
-}
-
-void negotiate_checksum(int f_in, int f_out, const char *csum_list, int fail_if_empty)
-{
-       char *tok, sumbuf[MAX_CHECKSUM_LIST];
-       uchar saw[CSUM_COUNT];
-       struct csum_struct *cs;
-       int len;
-
-       memset(saw, 0, sizeof saw);
-       for (len = 1, cs = valid_checksums; cs->name; len++, cs++) {
-               assert(len <= CSUM_COUNT);
-               if (saw[cs->num])
-                       cs->main_name = valid_checksums[saw[cs->num]-1].name;
-               else
-                       saw[cs->num] = len;
-       }
-       memset(saw, 0, sizeof saw);
-
-       /* Simplify the user-provided string so that it contains valid
-        * checksum names without any duplicates. The client side also
-        * makes use of the saw values when scanning the server's list. */
-       if (csum_list && *csum_list && (!am_server || local_server)) {
-               len = parse_checksum_list(csum_list, sumbuf, sizeof sumbuf, saw);
-               if (fail_if_empty && !len)
-                       len = strlcpy(sumbuf, "FAIL", sizeof sumbuf);
-               csum_list = sumbuf;
-       } else
-               csum_list = NULL;
-
-       if (!csum_list || !*csum_list) {
-               int cnt = 0;
-               for (cs = valid_checksums, len = 0; cs->name; cs++) {
-                       if (cs->num == CSUM_NONE || cs->main_name)
-                               continue;
-                       if (len)
-                               sumbuf[len++]= ' ';
-                       len += strlcpy(sumbuf+len, cs->name, sizeof sumbuf - len);
-                       if (len >= (int)sizeof sumbuf - 1)
-                               exit_cleanup(RERR_UNSUPPORTED); /* IMPOSSIBLE... */
-                       saw[cs->num] = ++cnt;
-               }
-       }
-
-       if (!am_server && DEBUG_GTE(CSUM, 2))
-               rprintf(FINFO, "Client checksum list: %s\n", sumbuf);
-
-       /* Each side sends their list of valid checksum names to the other side and
-        * then both sides pick the first name in the client's list that is also in
-        * the server's list. */
-       if (!local_server)
-               write_vstring(f_out, sumbuf, len);
-
-       if (!local_server || read_batch)
-               len = read_vstring(f_in, sumbuf, sizeof sumbuf);
-
-       if (!am_server && DEBUG_GTE(CSUM, 2))
-               rprintf(FINFO, "Server checksum list: %s\n", sumbuf);
-
-       if (len > 0) {
-               int best = CSUM_COUNT+1; /* We want best == 1 from the client list, so start with a big number. */
-               if (am_server)
-                       memset(saw, 1, sizeof saw); /* Since we're parsing client names, anything we parse first is #1. */
-               for (tok = strtok(sumbuf, " \t"); tok; tok = strtok(NULL, " \t")) {
-                       cs = parse_csum_name(tok, -1, 0);
-                       if (!cs || !saw[cs->num] || best <= saw[cs->num])
-                               continue;
-                       xfersum_type = checksum_type = cs->num;
-                       negotiated_csum_name = cs->main_name ? cs->main_name : cs->name;
-                       best = saw[cs->num];
-                       if (best == 1)
-                               break;
-               }
-               if (negotiated_csum_name)
-                       return;
-       }
-
-       if (!am_server)
-               rprintf(FERROR, "Failed to negotiate a common checksum\n");
-       exit_cleanup(RERR_UNSUPPORTED);
-}
-
 int csum_len_for_type(int cst, BOOL flist_csum)
 {
        switch (cst) {
@@ -525,7 +381,8 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
          }
 #endif
          default:
-               rprintf(FERROR, "invalid checksum-choice for the --checksum option (%d)\n", checksum_type);
+               rprintf(FERROR, "Invalid checksum-choice for --checksum: %s (%d)\n",
+                       checksum_name(checksum_type), checksum_type);
                exit_cleanup(RERR_UNSUPPORTED);
        }
 
@@ -551,7 +408,7 @@ void sum_init(int csum_type, int seed)
        char s[4];
 
        if (csum_type < 0)
-               csum_type = parse_csum_name(NULL, 0, 1)->num;
+               csum_type = parse_csum_name(NULL, 0);
        cursum_type = csum_type;
 
        switch (csum_type) {
index 8c77ea6934da5b4a5e5310a2761ffe28fcb266dd..04d8b8efb9deb25e13af13128817538d06cbcd75 100644 (file)
--- a/compat.c
+++ b/compat.c
 
 #include "rsync.h"
 
-int remote_protocol = 0;
-int file_extra_cnt = 0; /* count of file-list extras that everyone gets */
-int inc_recurse = 0;
-int compat_flags = 0;
-int use_safe_inc_flist = 0;
-int want_xattr_optim = 0;
-int proper_seed_order = 0;
-int inplace_partial = 0;
-
 extern int am_server;
 extern int am_sender;
 extern int local_server;
@@ -56,19 +47,33 @@ extern int preserve_xattrs;
 extern int xfer_flags_as_varint;
 extern int need_messages_from_generator;
 extern int delete_mode, delete_before, delete_during, delete_after;
+extern int xfersum_type;
+extern int checksum_type;
+extern int do_compression;
 extern char *shell_cmd;
 extern char *partial_dir;
 extern char *dest_option;
 extern char *files_from;
 extern char *filesfrom_host;
 extern char *checksum_choice;
+extern char *compress_choice;
 extern filter_rule_list filter_list;
 extern int need_unsorted_flist;
 #ifdef ICONV_OPTION
 extern iconv_t ic_send, ic_recv;
 extern char *iconv_opt;
 #endif
-extern const char *negotiated_csum_name;
+extern struct name_num_obj valid_checksums;
+
+int remote_protocol = 0;
+int file_extra_cnt = 0; /* count of file-list extras that everyone gets */
+int inc_recurse = 0;
+int compat_flags = 0;
+int use_safe_inc_flist = 0;
+int want_xattr_optim = 0;
+int proper_seed_order = 0;
+int inplace_partial = 0;
+int do_negotiated_strings = 0;
 
 /* These index values are for the file-list's extra-attribute array. */
 int pathname_ndx, depth_ndx, atimes_ndx, uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
@@ -80,6 +85,19 @@ int sender_symlink_iconv = 0;        /* sender should convert symlink content */
 int filesfrom_convert = 0;
 #endif
 
+#define MAX_NSTR_STRLEN 256
+
+#define CPRES_NONE 0
+#define CPRES_ZLIB 1
+
+struct name_num_obj valid_compressions = {
+       "compress", NULL, NULL, 0, 0, {
+               { CPRES_ZLIB, "zlib", NULL },
+               { CPRES_NONE, "none", "" }, /* The "" prevents us from listing this name by default */
+               { 0, NULL, NULL }
+       }
+};
+
 #define CF_INC_RECURSE  (1<<0)
 #define CF_SYMLINK_TIMES (1<<1)
 #define CF_SYMLINK_ICONV (1<<2)
@@ -142,10 +160,227 @@ void set_allow_inc_recurse(void)
                allow_inc_recurse = 0;
 }
 
-void setup_protocol(int f_out,int f_in)
+struct name_num_item *get_nni_by_name(struct name_num_obj *nno, const char *name, int len)
+{
+       struct name_num_item *nni;
+
+       if (len < 0)
+               len = strlen(name);
+
+       for (nni = nno->list; nni->name; nni++) {
+               if (strncasecmp(name, nni->name, len) == 0 && nni->name[len] == '\0')
+                       return nni;
+       }
+
+       return NULL;
+}
+
+struct name_num_item *get_nni_by_num(struct name_num_obj *nno, int num)
+{
+       struct name_num_item *nni;
+
+       for (nni = nno->list; nni->name; nni++) {
+               if (num == nni->num)
+                       return nni;
+       }
+
+       return NULL;
+}
+
+static void init_nno_saw(struct name_num_obj *nno, int val)
+{
+       struct name_num_item *nni;
+       int cnt;
+
+       if (!nno->saw_len) {
+               for (nni = nno->list; nni->name; nni++) {
+                       if (nni->num >= nno->saw_len)
+                               nno->saw_len = nni->num + 1;
+               }
+       }
+
+       if (!nno->saw) {
+               if (!(nno->saw = new_array0(uchar, nno->saw_len)))
+                       out_of_memory("init_nno_saw");
+
+               /* We'll take this opportunity to make sure that the main_name values are set right. */
+               for (cnt = 1, nni = nno->list; nni->name; nni++, cnt++) {
+                       if (nno->saw[nni->num])
+                               nni->main_name = nno->list[nno->saw[nni->num]-1].name;
+                       else
+                               nno->saw[nni->num] = cnt;
+               }
+       }
+
+       memset(nno->saw, val, nno->saw_len);
+}
+
+/* Simplify the user-provided string so that it contains valid names without any duplicates.
+ * It also sets the "saw" flags to a 1-relative count of which name was seen first. */
+static int parse_nni_str(struct name_num_obj *nno, const char *from, char *tobuf, int tobuf_len)
+{
+       char *to = tobuf, *tok = NULL;
+       int cnt = 0;
+
+       while (1) {
+               if (*from == ' ' || !*from) {
+                       if (tok) {
+                               struct name_num_item *nni = get_nni_by_name(nno, tok, to - tok);
+                               if (nni && !nno->saw[nni->num]) {
+                                       nno->saw[nni->num] = ++cnt;
+                                       if (nni->main_name && *nni->main_name) {
+                                               to = tok + strlcpy(tok, nni->main_name, tobuf_len - (tok - tobuf));
+                                               if (to - tobuf >= tobuf_len) {
+                                                       to = tok - 1;
+                                                       break;
+                                               }
+                                       } else
+                                               nni->main_name = NULL; /* Override a "" entry */
+                               } else
+                                       to = tok - (tok != tobuf);
+                               tok = NULL;
+                       }
+                       if (!*from++)
+                               break;
+                       continue;
+               }
+               if (!tok) {
+                       if (to != tobuf)
+                               *to++ = ' ';
+                       tok = to;
+               }
+               if (to - tobuf >= tobuf_len - 1) {
+                       to = tok - (tok != tobuf);
+                       break;
+               }
+               *to++ = *from++;
+       }
+       *to = '\0';
+
+       return to - tobuf;
+}
+
+static void recv_negotiate_str(int f_in, struct name_num_obj *nno, char *tmpbuf, int len)
+{
+       struct name_num_item *ret = NULL;
+
+       if (len < 0)
+               len = read_vstring(f_in, tmpbuf, MAX_NSTR_STRLEN);
+
+       if (DEBUG_GTE(NSTR, am_server ? 4 : 2))
+               rprintf(FINFO, "Server %s list: %s%s\n", nno->type, tmpbuf, am_server ? " (on server)" : "");
+
+       if (len > 0) {
+               int best = nno->saw_len; /* We want best == 1 from the client list, so start with a big number. */
+               char *tok;
+               if (am_server)
+                       init_nno_saw(nno, 1); /* Since we're parsing client names, anything we parse first is #1. */
+               for (tok = strtok(tmpbuf, " \t"); tok; tok = strtok(NULL, " \t")) {
+                       struct name_num_item *nni = get_nni_by_name(nno, tok, -1);
+                       if (!nni || !nno->saw[nni->num] || best <= nno->saw[nni->num])
+                               continue;
+                       ret = nni;
+                       best = nno->saw[nni->num];
+                       if (best == 1)
+                               break;
+               }
+               if (ret) {
+                       free(nno->saw);
+                       nno->saw = NULL;
+                       nno->negotiated_name = ret->main_name ? ret->main_name : ret->name;
+                       nno->negotiated_num = ret->num;
+                       return;
+               }
+       }
+
+       if (!am_server)
+               rprintf(FERROR, "Failed to negotiate a common %s\n", nno->type);
+       exit_cleanup(RERR_UNSUPPORTED);
+}
+
+static void send_negotiate_str(int f_out, struct name_num_obj *nno, const char *env_name)
+{
+       char tmpbuf[MAX_NSTR_STRLEN];
+       struct name_num_item *nni;
+       const char *list_str = getenv(env_name);
+       int len, fail_if_empty = list_str && strstr(list_str, "FAIL");
+
+       if (!do_negotiated_strings) {
+               if (!am_server && fail_if_empty) {
+                       rprintf(FERROR, "Remote rsync is too old for %s negotation\n", nno->type);
+                       exit_cleanup(RERR_UNSUPPORTED);
+               }
+               return;
+       }
+
+       init_nno_saw(nno, 0);
+
+       if (list_str && *list_str && (!am_server || local_server)) {
+               len = parse_nni_str(nno, list_str, tmpbuf, MAX_NSTR_STRLEN);
+               if (fail_if_empty && !len)
+                       len = strlcpy(tmpbuf, "FAIL", MAX_NSTR_STRLEN);
+               list_str = tmpbuf;
+       } else
+               list_str = NULL;
+
+       if (!list_str || !*list_str) {
+               int cnt = 0;
+               for (nni = nno->list, len = 0; nni->name; nni++) {
+                       if (nni->main_name)
+                               continue;
+                       if (len)
+                               tmpbuf[len++]= ' ';
+                       len += strlcpy(tmpbuf+len, nni->name, MAX_NSTR_STRLEN - len);
+                       if (len >= (int)MAX_NSTR_STRLEN - 1)
+                               exit_cleanup(RERR_UNSUPPORTED); /* IMPOSSIBLE... */
+                       nno->saw[nni->num] = ++cnt;
+               }
+       }
+
+       if (DEBUG_GTE(NSTR, am_server ? 4 : 2))
+               rprintf(FINFO, "Client %s list: %s%s\n", nno->type, tmpbuf, am_server ? " (on server)" : "");
+
+       if (local_server) {
+               /* A local server doesn't bother to send/recv the strings, it just constructs
+                * and parses the same string on both sides. */
+               if (!read_batch)
+                       recv_negotiate_str(-1, nno, tmpbuf, len);
+       } else {
+               /* Each side sends their list of valid names to the other side and then both sides
+                * pick the first name in the client's list that is also in the server's list. */
+               write_vstring(f_out, tmpbuf, len);
+       }
+}
+
+static void negotiate_the_strings(int f_in, int f_out)
 {
-       int csum_exchange = 0;
+       /* We send all the negotiation strings before we start to read them to help avoid a slow startup. */
 
+       if (!checksum_choice)
+               send_negotiate_str(f_out, &valid_checksums, "RSYNC_CHECKSUM_LIST");
+
+       if (do_compression && !compress_choice)
+               send_negotiate_str(f_out, &valid_compressions, "RSYNC_COMPRESS_LIST");
+
+       if (valid_checksums.saw) {
+               char tmpbuf[MAX_NSTR_STRLEN];
+               recv_negotiate_str(f_in, &valid_checksums, tmpbuf, -1);
+       }
+       if (valid_checksums.negotiated_name)
+               xfersum_type = checksum_type = valid_checksums.negotiated_num;
+
+       if (valid_compressions.saw) {
+               char tmpbuf[MAX_NSTR_STRLEN];
+               recv_negotiate_str(f_in, &valid_compressions, tmpbuf, -1);
+       }
+#if 0
+       if (valid_compressions.negotiated_name)
+               compress_type = valid_checksums.negotiated_num;
+#endif
+}
+
+void setup_protocol(int f_out,int f_in)
+{
        assert(file_extra_cnt == 0);
        assert(EXTRA64_CNT == 2 || EXTRA64_CNT == 1);
 
@@ -296,7 +531,7 @@ void setup_protocol(int f_out,int f_in)
                                compat_flags |= CF_INPLACE_PARTIAL_DIR;
                        if (local_server || strchr(client_info, 'v') != NULL) {
                                if (!write_batch || protocol_version >= 30) {
-                                       csum_exchange = 1;
+                                       do_negotiated_strings = 1;
                                        compat_flags |= CF_VARINT_FLIST_FLAGS;
                                }
                        }
@@ -309,7 +544,7 @@ void setup_protocol(int f_out,int f_in)
                } else { /* read_varint() is compatible with the older write_byte() when the 0x80 bit isn't on. */
                        compat_flags = read_varint(f_in);
                        if  (compat_flags & CF_VARINT_FLIST_FLAGS)
-                               csum_exchange = 1;
+                               do_negotiated_strings = 1;
                }
                /* The inc_recurse var MUST be set to 0 or 1. */
                inc_recurse = compat_flags & CF_INC_RECURSE ? 1 : 0;
@@ -367,16 +602,7 @@ void setup_protocol(int f_out,int f_in)
        }
 #endif
 
-       if (!checksum_choice) {
-               const char *rcl = getenv("RSYNC_CHECKSUM_LIST");
-               int saw_fail = rcl && strstr(rcl, "FAIL");
-               if (csum_exchange)
-                       negotiate_checksum(f_in, f_out, rcl, saw_fail);
-               else if (!am_server && saw_fail) {
-                       rprintf(FERROR, "Remote rsync is too old for checksum negotation\n");
-                       exit_cleanup(RERR_UNSUPPORTED);
-               }
-       }
+       negotiate_the_strings(f_in, f_out);
 
        if (am_server) {
                if (!checksum_seed)
@@ -389,9 +615,11 @@ void setup_protocol(int f_out,int f_in)
        init_flist();
 }
 
-void maybe_write_checksum(int batch_fd)
+void maybe_write_negotiated_strings(int batch_fd)
 {
-       assert(negotiated_csum_name != NULL);
-       if (compat_flags & CF_VARINT_FLIST_FLAGS)
-               write_vstring(batch_fd, negotiated_csum_name, strlen(negotiated_csum_name));
+       if (valid_checksums.negotiated_name)
+               write_vstring(batch_fd, valid_checksums.negotiated_name, strlen(valid_checksums.negotiated_name));
+
+       if (valid_compressions.negotiated_name)
+               write_vstring(batch_fd, valid_compressions.negotiated_name, strlen(valid_compressions.negotiated_name));
 }
diff --git a/io.c b/io.c
index 189bc23237916b35b68d799db9a9e1da5354fd10..021330fb3044f7c3a5a0f12b0c2133d679f883ab 100644 (file)
--- a/io.c
+++ b/io.c
@@ -2369,7 +2369,7 @@ void start_write_batch(int fd)
        write_int(batch_fd, protocol_version);
        if (protocol_version >= 30)
                write_varint(batch_fd, compat_flags);
-       maybe_write_checksum(batch_fd);
+       maybe_write_negotiated_strings(batch_fd);
        write_int(batch_fd, checksum_seed);
 
        if (am_sender)
index 959c420511285f07e8d383dbe1fc7cf3031a4b0b..62fe0fb528dbce1d35b5749a65da902fea78836e 100644 (file)
--- a/options.c
+++ b/options.c
@@ -188,6 +188,7 @@ static int remote_option_alloc = 0;
 int remote_option_cnt = 0;
 const char **remote_options = NULL;
 const char *checksum_choice = NULL;
+const char *compress_choice = NULL;
 
 int quiet = 0;
 int output_motd = 1;
@@ -276,7 +277,6 @@ static struct output_struct debug_words[COUNT_DEBUG+1] = {
        DEBUG_WORD(CHDIR, W_CLI|W_SRV, "Debug when the current directory changes"),
        DEBUG_WORD(CONNECT, W_CLI, "Debug connection events (levels 1-2)"),
        DEBUG_WORD(CMD, W_CLI, "Debug commands+options that are issued (levels 1-2)"),
-       DEBUG_WORD(CSUM, W_CLI|W_SRV, "Debug checksum negotiation"),
        DEBUG_WORD(DEL, W_REC, "Debug delete actions (levels 1-3)"),
        DEBUG_WORD(DELTASUM, W_SND|W_REC, "Debug delta-transfer checksumming (levels 1-4)"),
        DEBUG_WORD(DUP, W_REC, "Debug weeding of duplicate names"),
@@ -289,6 +289,7 @@ static struct output_struct debug_words[COUNT_DEBUG+1] = {
        DEBUG_WORD(HLINK, W_SND|W_REC, "Debug hard-link actions (levels 1-3)"),
        DEBUG_WORD(ICONV, W_CLI|W_SRV, "Debug iconv character conversions (levels 1-2)"),
        DEBUG_WORD(IO, W_CLI|W_SRV, "Debug I/O routines (levels 1-4)"),
+       DEBUG_WORD(NSTR, W_CLI|W_SRV, "Debug negotiation strings"),
        DEBUG_WORD(OWN, W_REC, "Debug ownership changes in users & groups (levels 1-2)"),
        DEBUG_WORD(PROTO, W_CLI|W_SRV, "Debug protocol information"),
        DEBUG_WORD(RECV, W_REC, "Debug receiver functions"),
@@ -451,7 +452,7 @@ static void parse_output_words(struct output_struct *words, short *levels,
                                        break;
                        }
                }
-               if (len && !words[j].name) {
+               if (len && !words[j].name && !am_server) {
                        rprintf(FERROR, "Unknown %s item: \"%.*s\"\n",
                                words[j].help, len, str);
                        exit_cleanup(RERR_SYNTAX);
diff --git a/rsync.h b/rsync.h
index d6c5de52f62c91b42f31bb0cdc475d9ffce5cc1c..5cf75fa7e38b2dbda1c03ed893cc5b963df5cb1f 100644 (file)
--- a/rsync.h
+++ b/rsync.h
@@ -1061,6 +1061,20 @@ typedef struct {
 #define ACL_READY(sx) ((sx).acc_acl != NULL)
 #define XATTR_READY(sx) ((sx).xattr != NULL)
 
+struct name_num_item {
+       int num;
+       const char *name, *main_name;
+};
+
+struct name_num_obj {
+       const char *type;
+       const char *negotiated_name;
+       uchar *saw;
+       int saw_len;
+       int negotiated_num;
+       struct name_num_item list[];
+};
+
 #ifndef __cplusplus
 #include "proto.h"
 #endif
@@ -1090,7 +1104,6 @@ int vsnprintf(char *str, size_t count, const char *fmt, va_list args);
 int snprintf(char *str, size_t count, const char *fmt,...);
 #endif
 
-
 #ifndef HAVE_STRERROR
 extern char *sys_errlist[];
 #define strerror(i) sys_errlist[i]
@@ -1317,8 +1330,7 @@ extern short info_levels[], debug_levels[];
 #define DEBUG_CHDIR (DEBUG_BIND+1)
 #define DEBUG_CONNECT (DEBUG_CHDIR+1)
 #define DEBUG_CMD (DEBUG_CONNECT+1)
-#define DEBUG_CSUM (DEBUG_CMD+1)
-#define DEBUG_DEL (DEBUG_CSUM+1)
+#define DEBUG_DEL (DEBUG_CMD+1)
 #define DEBUG_DELTASUM (DEBUG_DEL+1)
 #define DEBUG_DUP (DEBUG_DELTASUM+1)
 #define DEBUG_EXIT (DEBUG_DUP+1)
@@ -1330,7 +1342,8 @@ extern short info_levels[], debug_levels[];
 #define DEBUG_HLINK (DEBUG_HASH+1)
 #define DEBUG_ICONV (DEBUG_HLINK+1)
 #define DEBUG_IO (DEBUG_ICONV+1)
-#define DEBUG_OWN (DEBUG_IO+1)
+#define DEBUG_NSTR (DEBUG_IO+1)
+#define DEBUG_OWN (DEBUG_NSTR+1)
 #define DEBUG_PROTO (DEBUG_OWN+1)
 #define DEBUG_RECV (DEBUG_PROTO+1)
 #define DEBUG_SEND (DEBUG_RECV+1)