Ensure that the generator gets notified about an I/O error for the dir
authorWayne Davison <wayned@samba.org>
Mon, 21 Dec 2009 22:27:55 +0000 (14:27 -0800)
committerWayne Davison <wayned@samba.org>
Mon, 21 Dec 2009 22:40:41 +0000 (14:40 -0800)
that generated the error.  This ensures that a --delete-during avoids
deleting in a newly transferred inc-recurse directory.  Requires 3.0.7
or greater on both sides of the transfer.

NEWS
compat.c
flist.c
options.c
rsync.h

diff --git a/NEWS b/NEWS
index 87ffb2a5db1ee241c4a08e518e4e4259fb6ab7db..7f9bf1884f630997dad47d860d0f6e17a4ba2c54 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,10 @@ Changes since 3.0.6:
     - Fixed a problem with --compress (-z) where the receiving side could
       return the error "inflate (token) returned -5".
 
+    - Fixed a bug where --delete-during could delete in a directory before it
+      noticed that the sending side sent an I/O error for that directory (both
+      sides of the transfer must be at least 3.0.7).
+
     - Improved --skip-compress's error handling of bad character-sets and got
       rid of a lingering debug fprintf().
 
index b921d4e90d21d600be8d19506990dc24e393e4d6..c2b475ba6384cd79deb53a381084f19aae20624c 100644 (file)
--- a/compat.c
+++ b/compat.c
@@ -24,6 +24,7 @@
 int remote_protocol = 0;
 int file_extra_cnt = 0; /* count of file-list extras that everyone gets */
 int inc_recurse = 0;
+int use_safe_inc_flist = 0;
 
 extern int verbose;
 extern int am_server;
@@ -73,6 +74,7 @@ int filesfrom_convert = 0;
 #define CF_INC_RECURSE  (1<<0)
 #define CF_SYMLINK_TIMES (1<<1)
 #define CF_SYMLINK_ICONV (1<<2)
+#define CF_SAFE_FLIST   (1<<3)
 
 static const char *client_info;
 
@@ -255,6 +257,8 @@ void setup_protocol(int f_out,int f_in)
 #ifdef ICONV_OPTION
                        compat_flags |= CF_SYMLINK_ICONV;
 #endif
+                       if (local_server || strchr(client_info, 'f') != NULL)
+                               compat_flags |= CF_SAFE_FLIST;
                        write_byte(f_out, compat_flags);
                } else
                        compat_flags = read_byte(f_in);
@@ -281,6 +285,7 @@ void setup_protocol(int f_out,int f_in)
                            read_batch ? "batch file" : "connection");
                        exit_cleanup(RERR_SYNTAX);
                }
+               use_safe_inc_flist = !!(compat_flags & CF_SAFE_FLIST);
                need_messages_from_generator = 1;
 #if defined HAVE_LUTIMES && defined HAVE_UTIMES
        } else if (!am_sender) {
diff --git a/flist.c b/flist.c
index 7b8d55f53d914d021733fc0173c707f73b55eff1..7139b101bfb197a627096b654cea881ff7525b52 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;
@@ -1784,6 +1786,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 +1910,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 +2216,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 +2266,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 +2339,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)) {
index 5f0e8fc5d580d4632902a6dca7e92adfa1f192d8..2daaa67764e8ad78f9c3bbf8ac1fb2bb5bcf4c3a 100644 (file)
--- a/options.c
+++ b/options.c
@@ -1837,6 +1837,7 @@ void server_options(char **args, int *argc_p)
 #ifdef ICONV_OPTION
                argstr[x++] = 's';
 #endif
+               argstr[x++] = 'f';
        }
 
        if (x >= (int)sizeof argstr) { /* Not possible... */
diff --git a/rsync.h b/rsync.h
index c75fbc24e15b0fe44b4bd9efafbed559f2aa0542..5b20c5f323ed282a71be93d9270a81eb10cd3611 100644 (file)
--- a/rsync.h
+++ b/rsync.h
@@ -60,6 +60,7 @@
 #define XMIT_RDEV_MINOR_8_pre30 (1<<11)        /* protocols 28 - 29  */
 #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
 #define XMIT_HLINK_FIRST (1<<12)       /* protocols 30 - now (HLINKED files only) */
+#define XMIT_IO_ERROR_ENDLIST (1<<12)  /* protocols 31*- now (w/XMIT_EXTENDED_FLAGS) (also protocol 30 w/'f' compat flag) */
 
 /* These flags are used in the live flist data. */