Skip to content

Commit 5183c0d

Browse files
committed
Add safety check for local --remove-source-files.
A local_server copy now includes the dev+ino info from the destination file so that the sender can make sure that it is not going to delete the destination file. Fixes mistakes such as: rsync -aiv --remove-source-files dir .
1 parent 706bff9 commit 5183c0d

File tree

7 files changed

+56
-14
lines changed

7 files changed

+56
-14
lines changed

NEWS.md

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
- Fixed a bug with the new file-list validation code when the last line of the
1111
[`--files-from`](rsync.1#opt) list is not terminated by a newline.
1212

13+
- Added a safety check that prevents the sender from removing destination files
14+
when a local copy using [`--remove-source-files`](rsync.1#opt) has some
15+
content that is shared between the sending & receiving hierarchies, including
16+
the case where the source dir & destination dir are identical.
17+
1318
- Fixed a bug in the internal MD4 checksum code that could cause the digest
1419
to be sporadically incorrect (the openssl version was/is fine).
1520

generator.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1819,7 +1819,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
18191819
goto cleanup;
18201820
return_with_success:
18211821
if (!dry_run)
1822-
send_msg_int(MSG_SUCCESS, ndx);
1822+
send_msg_success(fname, ndx);
18231823
goto cleanup;
18241824
}
18251825

hlink.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ int hard_link_check(struct file_struct *file, int ndx, char *fname,
446446
return -1;
447447

448448
if (remove_source_files == 1 && do_xfers)
449-
send_msg_int(MSG_SUCCESS, ndx);
449+
send_msg_success(fname, ndx);
450450

451451
return 1;
452452
}
@@ -519,7 +519,7 @@ void finish_hard_link(struct file_struct *file, const char *fname, int fin_ndx,
519519
if (val < 0)
520520
continue;
521521
if (remove_source_files == 1 && do_xfers)
522-
send_msg_int(MSG_SUCCESS, ndx);
522+
send_msg_success(fname, ndx);
523523
}
524524

525525
if (inc_recurse) {

io.c

+30-4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ extern int am_server;
4141
extern int am_sender;
4242
extern int am_receiver;
4343
extern int am_generator;
44+
extern int local_server;
4445
extern int msgs2stderr;
4546
extern int inc_recurse;
4647
extern int io_error;
@@ -84,6 +85,8 @@ int sock_f_out = -1;
8485
int64 total_data_read = 0;
8586
int64 total_data_written = 0;
8687

88+
char num_dev_ino_buf[4 + 8 + 8];
89+
8790
static struct {
8891
xbuf in, out, msg;
8992
int in_fd;
@@ -1064,6 +1067,24 @@ void send_msg_int(enum msgcode code, int num)
10641067
send_msg(code, numbuf, 4, -1);
10651068
}
10661069

1070+
void send_msg_success(const char *fname, int num)
1071+
{
1072+
if (local_server) {
1073+
STRUCT_STAT st;
1074+
1075+
if (DEBUG_GTE(IO, 1))
1076+
rprintf(FINFO, "[%s] send_msg_success(%d)\n", who_am_i(), num);
1077+
1078+
if (stat(fname, &st) < 0)
1079+
memset(&st, 0, sizeof (STRUCT_STAT));
1080+
SIVAL(num_dev_ino_buf, 0, num);
1081+
SIVAL64(num_dev_ino_buf, 4, st.st_dev);
1082+
SIVAL64(num_dev_ino_buf, 4+8, st.st_ino);
1083+
send_msg(MSG_SUCCESS, num_dev_ino_buf, sizeof num_dev_ino_buf, -1);
1084+
} else
1085+
send_msg_int(MSG_SUCCESS, num);
1086+
}
1087+
10671088
static void got_flist_entry_status(enum festatus status, int ndx)
10681089
{
10691090
struct file_list *flist = flist_for_ndx(ndx, "got_flist_entry_status");
@@ -1078,8 +1099,12 @@ static void got_flist_entry_status(enum festatus status, int ndx)
10781099

10791100
switch (status) {
10801101
case FES_SUCCESS:
1081-
if (remove_source_files)
1082-
send_msg_int(MSG_SUCCESS, ndx);
1102+
if (remove_source_files) {
1103+
if (local_server)
1104+
send_msg(MSG_SUCCESS, num_dev_ino_buf, sizeof num_dev_ino_buf, -1);
1105+
else
1106+
send_msg_int(MSG_SUCCESS, ndx);
1107+
}
10831108
/* FALL THROUGH */
10841109
case FES_NO_SEND:
10851110
#ifdef SUPPORT_HARD_LINKS
@@ -1574,14 +1599,15 @@ static void read_a_msg(void)
15741599
}
15751600
break;
15761601
case MSG_SUCCESS:
1577-
if (msg_bytes != 4) {
1602+
if (msg_bytes != (local_server ? 4+8+8 : 4)) {
15781603
invalid_msg:
15791604
rprintf(FERROR, "invalid multi-message %d:%lu [%s%s]\n",
15801605
tag, (unsigned long)msg_bytes, who_am_i(),
15811606
inc_recurse ? "/inc" : "");
15821607
exit_cleanup(RERR_STREAMIO);
15831608
}
1584-
val = raw_read_int();
1609+
raw_read_buf(num_dev_ino_buf, msg_bytes);
1610+
val = IVAL(num_dev_ino_buf, 0);
15851611
iobuf.in_multiplexed = 1;
15861612
if (am_generator)
15871613
got_flist_entry_status(FES_SUCCESS, val);

receiver.c

+5-7
Original file line numberDiff line numberDiff line change
@@ -439,9 +439,8 @@ static void handle_delayed_updates(char *local_name)
439439
"rename failed for %s (from %s)",
440440
full_fname(fname), partialptr);
441441
} else {
442-
if (remove_source_files
443-
|| (preserve_hard_links && F_IS_HLINKED(file)))
444-
send_msg_int(MSG_SUCCESS, ndx);
442+
if (remove_source_files || (preserve_hard_links && F_IS_HLINKED(file)))
443+
send_msg_success(fname, ndx);
445444
handle_partial_dir(partialptr, PDIR_DELETE);
446445
}
447446
}
@@ -698,7 +697,7 @@ int recv_files(int f_in, int f_out, char *local_name)
698697
if (!am_server)
699698
discard_receive_data(f_in, file);
700699
if (inc_recurse)
701-
send_msg_int(MSG_SUCCESS, ndx);
700+
send_msg_success(fname, ndx);
702701
continue;
703702
}
704703

@@ -926,9 +925,8 @@ int recv_files(int f_in, int f_out, char *local_name)
926925
case 2:
927926
break;
928927
case 1:
929-
if (remove_source_files || inc_recurse
930-
|| (preserve_hard_links && F_IS_HLINKED(file)))
931-
send_msg_int(MSG_SUCCESS, ndx);
928+
if (remove_source_files || inc_recurse || (preserve_hard_links && F_IS_HLINKED(file)))
929+
send_msg_success(fname, ndx);
932930
break;
933931
case 0: {
934932
enum logcode msgtype = redoing ? FERROR_XFER : FWARNING;

rsync.1.md

+4
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,10 @@ expand it.
18081808
Starting with 3.1.0, rsync will skip the sender-side removal (and output an
18091809
error) if the file's size or modify time has not stayed unchanged.
18101810

1811+
Starting with 3.2.6, a local rsync copy will ensure that the sender does
1812+
not remove a file the receiver just verified, such as when the user
1813+
accidentally makes the source and destination directory the same path.
1814+
18111815
0. `--delete`
18121816

18131817
This tells rsync to delete extraneous files from the receiving side (ones

sender.c

+9
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
extern int do_xfers;
2626
extern int am_server;
2727
extern int am_daemon;
28+
extern int local_server;
2829
extern int inc_recurse;
2930
extern int log_before_transfer;
3031
extern int stdout_format_has_i;
@@ -51,6 +52,7 @@ extern int file_old_total;
5152
extern BOOL want_progress_now;
5253
extern struct stats stats;
5354
extern struct file_list *cur_flist, *first_flist, *dir_flist;
55+
extern char num_dev_ino_buf[4 + 8 + 8];
5456

5557
BOOL extra_flist_sending_enabled;
5658

@@ -144,6 +146,13 @@ void successful_send(int ndx)
144146
goto failed;
145147
}
146148

149+
if (local_server
150+
&& (int64)st.st_dev == IVAL64(num_dev_ino_buf, 4)
151+
&& (int64)st.st_ino == IVAL64(num_dev_ino_buf, 4 + 8)) {
152+
rprintf(FERROR_XFER, "ERROR: Skipping sender remove of destination file: %s\n", fname);
153+
return;
154+
}
155+
147156
if (st.st_size != F_LENGTH(file) || st.st_mtime != file->modtime
148157
#ifdef ST_MTIME_NSEC
149158
|| (NSEC_BUMP(file) && (uint32)st.ST_MTIME_NSEC != F_MOD_NSEC(file))

0 commit comments

Comments
 (0)