summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--checksum.c209
-rw-r--r--compat.c284
-rw-r--r--io.c2
-rw-r--r--options.c5
-rw-r--r--rsync.h21
5 files changed, 310 insertions, 211 deletions
diff --git a/checksum.c b/checksum.c
index 1be17522..60e0b55f 100644
--- a/checksum.c
+++ b/checksum.c
@@ -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) {
diff --git a/compat.c b/compat.c
index 8c77ea69..04d8b8ef 100644
--- a/compat.c
+++ b/compat.c
@@ -21,15 +21,6 @@
#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 189bc232..021330fb 100644
--- 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)
diff --git a/options.c b/options.c
index 959c4205..62fe0fb5 100644
--- 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 d6c5de52..5cf75fa7 100644
--- 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)