- Fixed a bug with the new file-list validation code when the last line of the
[`--files-from`](rsync.1#opt) list is not terminated by a newline.
+- Added a safety check that prevents the sender from removing destination files
+ when a local copy using [`--remove-source-files`](rsync.1#opt) has some
+ content that is shared between the sending & receiving hierarchies, including
+ the case where the source dir & destination dir are identical.
+
- Fixed a bug in the internal MD4 checksum code that could cause the digest
to be sporadically incorrect (the openssl version was/is fine).
goto cleanup;
return_with_success:
if (!dry_run)
- send_msg_int(MSG_SUCCESS, ndx);
+ send_msg_success(fname, ndx);
goto cleanup;
}
return -1;
if (remove_source_files == 1 && do_xfers)
- send_msg_int(MSG_SUCCESS, ndx);
+ send_msg_success(fname, ndx);
return 1;
}
if (val < 0)
continue;
if (remove_source_files == 1 && do_xfers)
- send_msg_int(MSG_SUCCESS, ndx);
+ send_msg_success(fname, ndx);
}
if (inc_recurse) {
extern int am_sender;
extern int am_receiver;
extern int am_generator;
+extern int local_server;
extern int msgs2stderr;
extern int inc_recurse;
extern int io_error;
int64 total_data_read = 0;
int64 total_data_written = 0;
+char num_dev_ino_buf[4 + 8 + 8];
+
static struct {
xbuf in, out, msg;
int in_fd;
send_msg(code, numbuf, 4, -1);
}
+void send_msg_success(const char *fname, int num)
+{
+ if (local_server) {
+ STRUCT_STAT st;
+
+ if (DEBUG_GTE(IO, 1))
+ rprintf(FINFO, "[%s] send_msg_success(%d)\n", who_am_i(), num);
+
+ if (stat(fname, &st) < 0)
+ memset(&st, 0, sizeof (STRUCT_STAT));
+ SIVAL(num_dev_ino_buf, 0, num);
+ SIVAL64(num_dev_ino_buf, 4, st.st_dev);
+ SIVAL64(num_dev_ino_buf, 4+8, st.st_ino);
+ send_msg(MSG_SUCCESS, num_dev_ino_buf, sizeof num_dev_ino_buf, -1);
+ } else
+ send_msg_int(MSG_SUCCESS, num);
+}
+
static void got_flist_entry_status(enum festatus status, int ndx)
{
struct file_list *flist = flist_for_ndx(ndx, "got_flist_entry_status");
switch (status) {
case FES_SUCCESS:
- if (remove_source_files)
- send_msg_int(MSG_SUCCESS, ndx);
+ if (remove_source_files) {
+ if (local_server)
+ send_msg(MSG_SUCCESS, num_dev_ino_buf, sizeof num_dev_ino_buf, -1);
+ else
+ send_msg_int(MSG_SUCCESS, ndx);
+ }
/* FALL THROUGH */
case FES_NO_SEND:
#ifdef SUPPORT_HARD_LINKS
}
break;
case MSG_SUCCESS:
- if (msg_bytes != 4) {
+ if (msg_bytes != (local_server ? 4+8+8 : 4)) {
invalid_msg:
rprintf(FERROR, "invalid multi-message %d:%lu [%s%s]\n",
tag, (unsigned long)msg_bytes, who_am_i(),
inc_recurse ? "/inc" : "");
exit_cleanup(RERR_STREAMIO);
}
- val = raw_read_int();
+ raw_read_buf(num_dev_ino_buf, msg_bytes);
+ val = IVAL(num_dev_ino_buf, 0);
iobuf.in_multiplexed = 1;
if (am_generator)
got_flist_entry_status(FES_SUCCESS, val);
"rename failed for %s (from %s)",
full_fname(fname), partialptr);
} else {
- if (remove_source_files
- || (preserve_hard_links && F_IS_HLINKED(file)))
- send_msg_int(MSG_SUCCESS, ndx);
+ if (remove_source_files || (preserve_hard_links && F_IS_HLINKED(file)))
+ send_msg_success(fname, ndx);
handle_partial_dir(partialptr, PDIR_DELETE);
}
}
if (!am_server)
discard_receive_data(f_in, file);
if (inc_recurse)
- send_msg_int(MSG_SUCCESS, ndx);
+ send_msg_success(fname, ndx);
continue;
}
case 2:
break;
case 1:
- if (remove_source_files || inc_recurse
- || (preserve_hard_links && F_IS_HLINKED(file)))
- send_msg_int(MSG_SUCCESS, ndx);
+ if (remove_source_files || inc_recurse || (preserve_hard_links && F_IS_HLINKED(file)))
+ send_msg_success(fname, ndx);
break;
case 0: {
enum logcode msgtype = redoing ? FERROR_XFER : FWARNING;
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.
+ Starting with 3.2.6, a local rsync copy will ensure that the sender does
+ not remove a file the receiver just verified, such as when the user
+ accidentally makes the source and destination directory the same path.
+
0. `--delete`
This tells rsync to delete extraneous files from the receiving side (ones
extern int do_xfers;
extern int am_server;
extern int am_daemon;
+extern int local_server;
extern int inc_recurse;
extern int log_before_transfer;
extern int stdout_format_has_i;
extern BOOL want_progress_now;
extern struct stats stats;
extern struct file_list *cur_flist, *first_flist, *dir_flist;
+extern char num_dev_ino_buf[4 + 8 + 8];
BOOL extra_flist_sending_enabled;
goto failed;
}
+ if (local_server
+ && (int64)st.st_dev == IVAL64(num_dev_ino_buf, 4)
+ && (int64)st.st_ino == IVAL64(num_dev_ino_buf, 4 + 8)) {
+ rprintf(FERROR_XFER, "ERROR: Skipping sender remove of destination file: %s\n", fname);
+ return;
+ }
+
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))