Report all socket connection errors if we fail.
[rsync.git] / flist.c
diff --git a/flist.c b/flist.c
index 7b8d55f53d914d021733fc0173c707f73b55eff1..253dbe0ade44bc689acaee69704a4010fc923898 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -51,6 +51,7 @@ extern int preserve_links;
 extern int preserve_hard_links;
 extern int preserve_devices;
 extern int preserve_specials;
+extern int delete_during;
 extern int uid_ndx;
 extern int gid_ndx;
 extern int eol_nulls;
@@ -65,6 +66,7 @@ extern int copy_unsafe_links;
 extern int protocol_version;
 extern int sanitize_paths;
 extern int munge_symlinks;
+extern int use_safe_inc_flist;
 extern int need_unsorted_flist;
 extern int sender_symlink_iconv;
 extern int unsort_ndx;
@@ -116,7 +118,7 @@ int flist_eof = 0; /* all the file-lists are now known */
  * will survive just long enough to be used by send_file_entry(). */
 static dev_t tmp_rdev;
 #ifdef SUPPORT_HARD_LINKS
-static int64 tmp_dev, tmp_ino;
+static int64 tmp_dev = -1, tmp_ino;
 #endif
 static char tmp_sum[MAX_DIGEST_LEN];
 
@@ -478,7 +480,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
                modtime = file->modtime;
 
 #ifdef SUPPORT_HARD_LINKS
-       if (tmp_dev != 0) {
+       if (tmp_dev != -1) {
                if (protocol_version >= 30) {
                        struct ht_int64_node *np = idev_find(tmp_dev, tmp_ino);
                        first_hlink_ndx = (int32)(long)np->data - 1;
@@ -596,15 +598,17 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
 #endif
 
 #ifdef SUPPORT_HARD_LINKS
-       if (tmp_dev != 0 && protocol_version < 30) {
+       if (tmp_dev != -1 && protocol_version < 30) {
+               /* Older protocols expect the dev number to be transmitted
+                * 1-incremented so that it is never zero. */
                if (protocol_version < 26) {
                        /* 32-bit dev_t and ino_t */
-                       write_int(f, (int32)dev);
+                       write_int(f, (int32)(dev+1));
                        write_int(f, (int32)tmp_ino);
                } else {
                        /* 64-bit dev_t and ino_t */
                        if (!(xflags & XMIT_SAME_DEV_pre30))
-                               write_longint(f, dev);
+                               write_longint(f, dev+1);
                        write_longint(f, tmp_ino);
                }
        }
@@ -1257,10 +1261,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                if (protocol_version >= 28
                 ? (!S_ISDIR(st.st_mode) && st.st_nlink > 1)
                 : S_ISREG(st.st_mode)) {
-                       tmp_dev = (int64)st.st_dev + 1;
+                       tmp_dev = (int64)st.st_dev;
                        tmp_ino = (int64)st.st_ino;
                } else
-                       tmp_dev = 0;
+                       tmp_dev = -1;
        }
 #endif
 
@@ -1474,12 +1478,6 @@ static void send_if_directory(int f, struct file_list *flist,
                unsigned int len = strlen(fbuf);
                if (len > 1 && fbuf[len-1] == '/')
                        fbuf[--len] = '\0';
-               if (len >= MAXPATHLEN - 1) {
-                       io_error |= IOERR_GENERAL;
-                       rprintf(FERROR_XFER, "skipping long-named directory: %s\n",
-                               full_fname(fbuf));
-                       return;
-               }
                save_filters = push_local_filters(fbuf, len);
                send_directory(f, flist, fbuf, len, flags);
                pop_local_filters(save_filters);
@@ -1638,21 +1636,29 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
        }
 
        p = fbuf + len;
-       if (len != 1 || *fbuf != '/')
+       if (len == 1 && *fbuf == '/')
+               remainder = MAXPATHLEN - 1;
+       else if (len < MAXPATHLEN-1) {
                *p++ = '/';
-       *p = '\0';
-       remainder = MAXPATHLEN - (p - fbuf);
+               *p = '\0';
+               remainder = MAXPATHLEN - (len + 1);
+       } else
+               remainder = 0;
 
        for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
                char *dname = d_name(di);
                if (dname[0] == '.' && (dname[1] == '\0'
                    || (dname[1] == '.' && dname[2] == '\0')))
                        continue;
-               if (strlcpy(p, dname, remainder) >= remainder) {
+               unsigned name_len = strlcpy(p, dname, remainder);
+               if (name_len >= remainder) {
+                       char save = fbuf[len];
+                       fbuf[len] = '\0';
                        io_error |= IOERR_GENERAL;
                        rprintf(FERROR_XFER,
-                               "cannot send long-named file %s\n",
-                               full_fname(fbuf));
+                               "filename overflows max-path len by %u: %s/%s\n",
+                               name_len - remainder + 1, fbuf, dname);
+                       fbuf[len] = save;
                        continue;
                }
                if (dname[0] == '\0') {
@@ -1784,6 +1790,15 @@ done:
        filter_list = save_filter_list;
 }
 
+static NORETURN void fatal_unsafe_io_error(void)
+{
+       /* This (sadly) can only happen when pushing data because
+        * the sender does not know about what kind of delete
+        * is in effect on the receiving side when pulling. */
+       rprintf(FERROR_XFER, "FATAL I/O ERROR: dying to avoid a --delete-during issue with a pre-3.0.7 receiver.\n");
+       exit_cleanup(RERR_UNSUPPORTED);
+}
+
 static void send1extra(int f, struct file_struct *file, struct file_list *flist)
 {
        char fbuf[MAXPATHLEN];
@@ -1899,7 +1914,16 @@ void send_extra_file_list(int f, int at_least)
                        dp = F_DIR_NODE_P(file);
                }
 
-               write_byte(f, 0);
+               if (io_error == save_io_error || ignore_errors)
+                       write_byte(f, 0);
+               else if (use_safe_inc_flist) {
+                       write_shortint(f, XMIT_EXTENDED_FLAGS|XMIT_IO_ERROR_ENDLIST);
+                       write_varint(f, io_error);
+               } else {
+                       if (delete_during)
+                               fatal_unsafe_io_error();
+                       write_byte(f, 0);
+               }
 
                if (need_unsorted_flist) {
                        if (!(flist->sorted = new_array(struct file_struct *, flist->used)))
@@ -2196,7 +2220,17 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                stats.flist_buildtime = 1;
        start_tv = end_tv;
 
-       write_byte(f, 0); /* Indicate end of file list */
+       /* Indicate end of file list */
+       if (io_error == 0 || ignore_errors)
+               write_byte(f, 0);
+       else if (use_safe_inc_flist) {
+               write_shortint(f, XMIT_EXTENDED_FLAGS|XMIT_IO_ERROR_ENDLIST);
+               write_varint(f, io_error);
+       } else {
+               if (delete_during && inc_recurse)
+                       fatal_unsafe_io_error();
+               write_byte(f, 0);
+       }
 
 #ifdef SUPPORT_HARD_LINKS
        if (preserve_hard_links && protocol_version >= 30 && !inc_recurse)
@@ -2236,7 +2270,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
        /* send the io_error flag */
        if (protocol_version < 30)
                write_int(f, ignore_errors ? 0 : io_error);
-       else if (io_error && !ignore_errors)
+       else if (!use_safe_inc_flist && io_error && !ignore_errors)
                send_msg_int(MSG_IO_ERROR, io_error);
 
        if (disable_buffering)
@@ -2309,10 +2343,22 @@ struct file_list *recv_file_list(int f)
        while ((flags = read_byte(f)) != 0) {
                struct file_struct *file;
 
-               flist_expand(flist, 1);
-
                if (protocol_version >= 28 && (flags & XMIT_EXTENDED_FLAGS))
                        flags |= read_byte(f) << 8;
+
+               if (flags == (XMIT_EXTENDED_FLAGS|XMIT_IO_ERROR_ENDLIST)) {
+                       int err;
+                       if (!use_safe_inc_flist) {
+                               rprintf(FERROR, "Invalid flist flag: %x\n", flags);
+                               exit_cleanup(RERR_PROTOCOL);
+                       }
+                       err = read_varint(f);
+                       if (!ignore_errors)
+                               io_error |= err;
+                       break;
+               }
+
+               flist_expand(flist, 1);
                file = recv_file_entry(flist, flags, f);
 
                if (inc_recurse && S_ISDIR(file->mode)) {