Generate a helpful message when we get an option-error from a daemon
[rsync.git] / io.c
diff --git a/io.c b/io.c
index afe837f1828acd43cfd39edc89403b1df513eb84..ce675d202b55c9689c828ad5645d09b4b6cf8199 100644 (file)
--- a/io.c
+++ b/io.c
@@ -4,7 +4,7 @@
  * Copyright (C) 1996-2001 Andrew Tridgell
  * Copyright (C) 1996 Paul Mackerras
  * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
- * Copyright (C) 2003-2007 Wayne Davison
+ * Copyright (C) 2003-2008 Wayne Davison
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -45,6 +45,7 @@ extern int inc_recurse;
 extern int io_error;
 extern int eol_nulls;
 extern int flist_eof;
+extern int list_only;
 extern int read_batch;
 extern int csum_length;
 extern int protect_args;
@@ -104,6 +105,7 @@ static int defer_forwarding_messages = 0, defer_forwarding_keep = 0;
 static int select_timeout = SELECT_TIMEOUT;
 static int active_filecnt = 0;
 static OFF_T active_bytecnt = 0;
+static int first_message = 1;
 
 static char int_byte_extra[64] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* (00 - 3F)/4 */
@@ -112,6 +114,9 @@ static char int_byte_extra[64] = {
        2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 6, /* (C0 - FF)/4 */
 };
 
+#define REMOTE_OPTION_ERROR "rsync: on remote machine: -"
+#define REMOTE_OPTION_ERROR2 ": unknown option"
+
 enum festatus { FES_SUCCESS, FES_REDO, FES_NO_SEND };
 
 static void readfd(int fd, char *buffer, size_t N);
@@ -321,6 +326,37 @@ static void msg_flush(void)
        }
 }
 
+static void check_for_d_option_error(const char *msg)
+{
+       static char rsync263_opts[] = "BCDHIKLPRSTWabceghlnopqrtuvxz";
+       char *colon;
+       int saw_d = 0;
+
+       if (*msg != 'r'
+        || strncmp(msg, REMOTE_OPTION_ERROR, sizeof REMOTE_OPTION_ERROR - 1) != 0)
+               return;
+
+       msg += sizeof REMOTE_OPTION_ERROR - 1;
+       if (*msg == '-' || (colon = strchr(msg, ':')) == NULL
+        || strncmp(colon, REMOTE_OPTION_ERROR2, sizeof REMOTE_OPTION_ERROR2 - 1) != 0)
+               return;
+
+       for ( ; *msg != ':'; msg++) {
+               if (*msg == 'd')
+                       saw_d = 1;
+               else if (*msg == 'e')
+                       break;
+               else if (strchr(rsync263_opts, *msg) == NULL)
+                       return;
+       }
+
+       if (saw_d) {
+               rprintf(FWARNING,
+                       "*** Try adding \"-r --exclude='/*/*'\" "
+                       "if remote rsync is <= 2.6.3 ***\n");
+       }
+}
+
 /* Read a message from the MSG_* fd and handle it.  This is called either
  * during the early stages of being a local sender (up through the sending
  * of the file list) or when we're the generator (to fetch the messages
@@ -413,15 +449,17 @@ static void read_msg_fd(void)
                readfd(fd, buf, 4);
                got_flist_entry_status(FES_NO_SEND, buf);
                break;
-       case MSG_SOCKERR:
+       case MSG_ERROR_SOCKET:
        case MSG_CLIENT:
                if (!am_generator)
                        goto invalid_msg;
-               if (tag == MSG_SOCKERR)
+               if (tag == MSG_ERROR_SOCKET)
                        io_end_multiplex_out();
                /* FALL THROUGH */
        case MSG_INFO:
        case MSG_ERROR:
+       case MSG_ERROR_XFER:
+       case MSG_WARNING:
        case MSG_LOG:
                while (len) {
                        n = len;
@@ -468,41 +506,38 @@ static void mplex_write(int fd, enum msgcode code, const char *buf, size_t len,
        char buffer[BIGPATHBUFLEN]; /* Oversized for use by iconv code. */
        size_t n = len;
 
-       SIVAL(buffer, 0, ((MPLEX_BASE + (int)code)<<24) + len);
-
 #ifdef ICONV_OPTION
-       if (convert && ic_send == (iconv_t)-1)
-#endif
-               convert = 0;
+       /* We need to convert buf before doing anything else so that we
+        * can include the (converted) byte length in the message header. */
+       if (convert && ic_send != (iconv_t)-1) {
+               xbuf outbuf, inbuf;
 
-       if (convert || n > 1024 - 4) /* BIGPATHBUFLEN can handle 1024 bytes */
-               n = 0;
+               INIT_XBUF(outbuf, buffer + 4, 0, sizeof buffer - 4);
+               INIT_XBUF(inbuf, (char*)buf, len, -1);
+
+               iconvbufs(ic_send, &inbuf, &outbuf,
+                         ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE);
+               if (inbuf.len > 0) {
+                       rprintf(FERROR, "overflowed conversion buffer in mplex_write");
+                       exit_cleanup(RERR_UNSUPPORTED);
+               }
+
+               n = len = outbuf.len;
+       } else
+#endif
+       if (n > 1024 - 4) /* BIGPATHBUFLEN can handle 1024 bytes */
+               n = 0;    /* We'd rather do 2 writes than too much memcpy(). */
        else
                memcpy(buffer + 4, buf, n);
 
+       SIVAL(buffer, 0, ((MPLEX_BASE + (int)code)<<24) + len);
+
        defer_forwarding_keep = 1; /* defer_forwarding_messages++ on return */
        writefd_unbuffered(fd, buffer, n+4);
        defer_forwarding_keep = 0;
 
-       len -= n;
-       buf += n;
-
-#ifdef ICONV_OPTION
-       if (convert) {
-               xbuf outbuf, inbuf;
-
-               INIT_CONST_XBUF(outbuf, buffer);
-               INIT_XBUF(inbuf, (char*)buf, len, -1);
-
-               do {
-                       iconvbufs(ic_send, &inbuf, &outbuf,
-                                 ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE);
-                       writefd_unbuffered(fd, outbuf.buf, outbuf.len);
-               } while (inbuf.len);
-       } else
-#endif
-       if (len)
-               writefd_unbuffered(fd, buf, len);
+       if (len > n)
+               writefd_unbuffered(fd, buf+n, len-n);
 
        if (!--defer_forwarding_messages && !no_flush)
                msg_flush();
@@ -754,7 +789,7 @@ static int read_timeout(int fd, char *buf, size_t len)
                        /* Don't write errors on a dead socket. */
                        if (fd == sock_f_in) {
                                io_end_multiplex_out();
-                               rsyserr(FSOCKERR, errno, "read error");
+                               rsyserr(FERROR_SOCKET, errno, "read error");
                        } else
                                rsyserr(FERROR, errno, "read error");
                        exit_cleanup(RERR_STREAMIO);
@@ -1060,7 +1095,7 @@ static int readfd_unbuffered(int fd, char *buf, size_t len)
                                int pos = 0;
 
                                INIT_CONST_XBUF(outbuf, line);
-                               inbuf.buf = ibuf;
+                               INIT_XBUF(inbuf, ibuf, 0, -1);
 
                                while (msg_bytes) {
                                        inbuf.len = msg_bytes > sizeof ibuf
@@ -1109,6 +1144,8 @@ static int readfd_unbuffered(int fd, char *buf, size_t len)
                        break;
                case MSG_INFO:
                case MSG_ERROR:
+               case MSG_ERROR_XFER:
+               case MSG_WARNING:
                        if (msg_bytes >= sizeof line) {
                            overflow:
                                rprintf(FERROR,
@@ -1118,6 +1155,13 @@ static int readfd_unbuffered(int fd, char *buf, size_t len)
                        }
                        read_loop(fd, line, msg_bytes);
                        rwrite((enum logcode)tag, line, msg_bytes, 1);
+                       if (first_message) {
+                               if (list_only && !am_sender && tag == 1) {
+                                       line[msg_bytes] = '\0';
+                                       check_for_d_option_error(line);
+                               }
+                               first_message = 0;
+                       }
                        break;
                default:
                        rprintf(FERROR, "unexpected tag %d [%s]\n",