Check if sender file changed before allowing a remove.
authorWayne Davison <wayned@samba.org>
Sat, 18 Jun 2011 19:06:44 +0000 (12:06 -0700)
committerWayne Davison <wayned@samba.org>
Sat, 18 Jun 2011 19:29:21 +0000 (12:29 -0700)
Fixes bug 7691.

flist.c
rsync.h
rsync.yo
sender.c
tls.c

diff --git a/flist.c b/flist.c
index 0758f8967d90a93723c1f57e40b04ea1f9f3fb7c..7a6ab342ee4e826c9a6542aafd4f277b50fb0f59 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -86,14 +86,6 @@ extern int filesfrom_convert;
 extern iconv_t ic_send, ic_recv;
 #endif
 
-#ifdef HAVE_UTIMENSAT
-#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
-#define ST_MTIME_NSEC st_mtim.tv_nsec
-#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
-#define ST_MTIME_NSEC st_mtimensec
-#endif
-#endif
-
 #define PTR_SIZE (sizeof (struct file_struct *))
 
 int io_error;
diff --git a/rsync.h b/rsync.h
index 84d1287cd7e48898c292e45228b8fc40d7260d42..f55e2f79deefc3ef9d59576edba166424d15a03e 100644 (file)
--- a/rsync.h
+++ b/rsync.h
@@ -383,6 +383,14 @@ enum delret {
 #define CAN_CHMOD_SYMLINK 1
 #endif
 
+#ifdef HAVE_UTIMENSAT
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+#define ST_MTIME_NSEC st_mtim.tv_nsec
+#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
+#define ST_MTIME_NSEC st_mtimensec
+#endif
+#endif
+
 #ifdef HAVE_SYS_SELECT_H
 #include <sys/select.h>
 #endif
index a1cac988adabd46faab562f4464b3b12043a212d..3174ca3708d5cf7d8a77526ef7da31cd3c1d80d2 100644 (file)
--- a/rsync.yo
+++ b/rsync.yo
@@ -1261,6 +1261,19 @@ dit(bf(--remove-source-files)) This tells rsync to remove from the sending
 side the files (meaning non-directories) that are a part of the transfer
 and have been successfully duplicated on the receiving side.
 
+Note that you should only use this option on source files that are quiescent.
+If you are using this to move files that show up in a particular directory over
+to another host, make sure that the finished files get renamed into the source
+directory, not directly written into it, so that rsync can't possibly transfer
+a file that is not yet fully written.  If you can't first write the files into
+a different directory, you should use a naming idiom that lets rsync avoid
+transferring files that are not yet finished (e.g. name the file "foo.new" when
+it is written, rename it to "foo" when it is done, and then use the option
+bf(--exclude='*.new') for the rsync transfer).
+
+Starting with 3.1.0, rsync will skip the sender-side removal (and output an
+error) if the file's size or modify time has not stayed unchanged.
+
 dit(bf(--delete)) This tells rsync to delete extraneous files from the
 receiving side (ones that aren't on the sending side), but only for the
 directories that are being synchronized.  You must have asked rsync to
index 7b6d3133d7be2d4cafc8ef4fdc8f2ea781787893..ebc8839f53672af09c904369d5c234428de957e8 100644 (file)
--- a/sender.c
+++ b/sender.c
@@ -123,8 +123,10 @@ static struct sum_struct *receive_sums(int f)
 void successful_send(int ndx)
 {
        char fname[MAXPATHLEN];
+       char *failed_op;
        struct file_struct *file;
        struct file_list *flist;
+       STRUCT_STAT st;
 
        if (!remove_source_files)
                return;
@@ -135,11 +137,31 @@ void successful_send(int ndx)
                return;
        f_name(file, fname);
 
-       if (do_unlink(fname) == 0) {
+       if (do_lstat(fname, &st) < 0) {
+               failed_op = "re-lstat";
+               goto failed;
+       }
+
+       if (st.st_size != F_LENGTH(file) || st.st_mtime != file->modtime
+#ifdef ST_MTIME_NSEC
+        || (NSEC_BUMP(file) && (uint32)st.ST_MTIME_NSEC != F_MOD_NSEC(file))
+#endif
+       ) {
+               rprintf(FERROR, "ERROR: Skipping sender remove for changed file: %s\n", fname);
+               return;
+       }
+
+       if (do_unlink(fname) < 0) {
+               failed_op = "remove";
+         failed:
+               if (errno == ENOENT)
+                       rprintf(FINFO, "sender file already removed: %s\n", fname);
+               else
+                       rsyserr(FERROR, errno, "sender failed to %s %s", failed_op, fname);
+       } else {
                if (INFO_GTE(REMOVE, 1))
                        rprintf(FINFO, "sender removed %s\n", fname);
-       } else
-               rsyserr(FERROR, errno, "sender failed to remove %s", fname);
+       }
 }
 
 static void write_ndx_and_attrs(int f_out, int ndx, int iflags,
diff --git a/tls.c b/tls.c
index aa43b912696564c9826f20753aa6ea9754903e3a..2dec05900671ded5c6bc816c27ba9873d559dca7 100644 (file)
--- a/tls.c
+++ b/tls.c
@@ -53,14 +53,6 @@ int preserve_perms = 0;
 int preserve_executability = 0;
 char number_separator;
 
-#ifdef HAVE_UTIMENSAT
-#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
-#define ST_MTIME_NSEC st_mtim.tv_nsec
-#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
-#define ST_MTIME_NSEC st_mtimensec
-#endif
-#endif
-
 #ifdef SUPPORT_XATTRS
 
 #ifdef HAVE_LINUX_XATTRS