PATCH: fast copy of files in local server mode

Amir Goldor amir73il at users.sourceforge.net
Wed Nov 4 12:19:20 MST 2009


 Dear List,

the attached patch makes rsync of local folders almost as fast as cp.
when rsync client and server has detected that they are working in
local_server mode,
they use local_socket, a unix domain socket pair, to pass the file
descriptors of the synced files.
the server uses the file descriptor it receives from the client to fast copy
from src to dst file.
on completion of every file fast copy the server ACKs the client back on the
local_socket,
that it is ready to receive another file descriptor for fast copy.

Enjoy,
Amir.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.samba.org/pipermail/rsync/attachments/20091104/89a3dba0/attachment.html>
-------------- next part --------------
diff -au rsync-3.0.6/io.c rsync-3.0.6.fast/io.c
--- rsync-3.0.6/io.c	2009-03-13 18:46:39.000000000 +0200
+++ rsync-3.0.6.fast/io.c	2009-09-23 12:26:40.000000000 +0300
@@ -29,6 +29,7 @@
 
 #include "rsync.h"
 #include "ifuncs.h"
+#include <linux/socket.h>
 
 /** If no timeout is specified then use a 60 second select timeout */
 #define SELECT_TIMEOUT 60
@@ -1722,6 +1723,82 @@
 		writefd(f, str, len);
 }
 
+int send_fd(int socket, int fd_to_send)
+{
+	struct msghdr message;
+	struct iovec iov[1];
+	struct cmsghdr *control_message = NULL;
+	char buffer[CMSG_SPACE(sizeof(int))], data[1];
+	int ret;
+
+	memset(&message, 0, sizeof(struct msghdr));
+	memset(buffer, 0, CMSG_SPACE(sizeof(int)));
+
+	data[0] = 'F';
+	iov[0].iov_base = data;
+	iov[0].iov_len = 1;
+
+	message.msg_iov = iov;
+	message.msg_iovlen = 1;
+
+	message.msg_control = buffer;
+	message.msg_controllen = CMSG_SPACE(sizeof(int));
+
+	control_message = CMSG_FIRSTHDR(&message);
+	control_message->cmsg_level = SOL_SOCKET;
+	control_message->cmsg_type = SCM_RIGHTS;
+	control_message->cmsg_len = CMSG_LEN(sizeof(int));
+
+	*((int *) CMSG_DATA(control_message)) = fd_to_send;
+
+	message.msg_controllen = control_message->cmsg_len;
+
+	do ret = sendmsg(socket, &message, 0);
+	while (ret == -1 && (errno == EINTR || errno == EAGAIN));
+	return ret;
+}
+
+int recv_fd(int socket)
+{
+	struct msghdr message;
+	struct iovec iov[1];
+	struct cmsghdr *control_message = NULL;
+	char buffer[CMSG_SPACE(sizeof(int))], data[1];
+	int ret;
+
+	memset(&message, 0, sizeof(struct msghdr));
+	memset(buffer, 0, CMSG_SPACE(sizeof(int)));
+
+	iov[0].iov_base = data;
+	iov[0].iov_len = 1;
+
+	message.msg_iov = iov;
+	message.msg_iovlen = 1;
+
+	message.msg_control = buffer;
+	message.msg_controllen = CMSG_SPACE(sizeof(int));
+
+	do ret = recvmsg(socket, &message, MSG_WAITALL);
+	while (ret == -1 && (errno == EINTR || errno == EAGAIN));
+	
+	if (ret <= 0)
+		return -1;
+
+	for(control_message = CMSG_FIRSTHDR(&message);
+			control_message != NULL;
+			control_message = CMSG_NXTHDR(&message,
+				control_message))
+	{
+		if( (control_message->cmsg_level == SOL_SOCKET) &&
+				(control_message->cmsg_type == SCM_RIGHTS) )
+		{
+			return *((int *) CMSG_DATA(control_message));
+		}
+	}
+
+	return -1;
+}
+
 /* Send a file-list index using a byte-reduction method. */
 void write_ndx(int f, int32 ndx)
 {
diff -au rsync-3.0.6/pipe.c rsync-3.0.6.fast/pipe.c
--- rsync-3.0.6/pipe.c	2009-01-17 23:41:35.000000000 +0200
+++ rsync-3.0.6.fast/pipe.c	2009-09-23 14:40:42.000000000 +0300
@@ -29,6 +29,7 @@
 extern mode_t orig_umask;
 extern char *logfile_name;
 extern struct chmod_mode_struct *chmod_modes;
+int local_socket = 0;
 
 /**
  * Create a child connected to us via its stdin/stdout.
@@ -111,11 +112,15 @@
 	pid_t pid;
 	int to_child_pipe[2];
 	int from_child_pipe[2];
+	int child_socket[2];
 
 	/* The parent process is always the sender for a local rsync. */
 	assert(am_sender);
 
 	if (fd_pair(to_child_pipe) < 0 ||
+#ifdef HAVE_SOCKETPAIR
+		fd_pair(child_socket) < 0 ||
+#endif
 	    fd_pair(from_child_pipe) < 0) {
 		rsyserr(FERROR, errno, "pipe");
 		exit_cleanup(RERR_IPC);
@@ -141,6 +146,9 @@
 
 		if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
 		    close(to_child_pipe[1]) < 0 ||
+#ifdef HAVE_SOCKETPAIR
+			close(child_socket[1]) < 0 ||
+#endif
 		    close(from_child_pipe[0]) < 0 ||
 		    dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
 			rsyserr(FERROR, errno, "Failed to dup/close");
@@ -150,6 +158,9 @@
 			close(to_child_pipe[0]);
 		if (from_child_pipe[1] != STDOUT_FILENO)
 			close(from_child_pipe[1]);
+#ifdef HAVE_SOCKETPAIR
+		local_socket = child_socket[0];
+#endif
 #ifdef ICONV_CONST
 		setup_iconv();
 #endif
@@ -157,6 +168,9 @@
 	}
 
 	if (close(from_child_pipe[1]) < 0 ||
+#ifdef HAVE_SOCKETPAIR
+	    close(child_socket[0]) < 0 ||
+#endif
 	    close(to_child_pipe[0]) < 0) {
 		rsyserr(FERROR, errno, "Failed to close");
 		exit_cleanup(RERR_IPC);
@@ -164,6 +178,9 @@
 
 	*f_in = from_child_pipe[0];
 	*f_out = to_child_pipe[1];
+#ifdef HAVE_SOCKETPAIR
+	local_socket = child_socket[1];
+#endif
 
 	return pid;
 }
diff -au rsync-3.0.6/receiver.c rsync-3.0.6.fast/receiver.c
--- rsync-3.0.6/receiver.c	2009-04-12 22:48:59.000000000 +0300
+++ rsync-3.0.6.fast/receiver.c	2009-09-23 17:05:35.000000000 +0300
@@ -35,6 +35,8 @@
 extern int write_batch;
 extern int batch_gen_fd;
 extern int protocol_version;
+extern int local_server;
+extern int local_socket;
 extern int relative_paths;
 extern int preserve_hard_links;
 extern int preserve_perms;
@@ -172,12 +174,24 @@
 	int32 len, sum_len;
 	OFF_T offset = 0;
 	OFF_T offset2;
-	char *data;
+	char *data = NULL;
 	int32 i;
 	char *map = NULL;
+	int percent = 0;
 
 	read_sum_head(f_in, &sum);
 
+	if (local_server && local_socket && sum.count == 0) {
+		int i = recv_fd(local_socket);
+		rprintf(FINFO,"received file descriptor %d\n", i); 
+		if (i <= 0)
+			offset = -1;
+		write_buf(local_socket, (char *)&offset, sizeof(offset));
+		if (i <= 0)
+			return 0;
+		f_in = -i;
+	}
+
 	if (fd_r >= 0 && size_r > 0) {
 		int32 read_size = MAX(sum.blength * 2, 16*1024);
 		mapbuf = map_file(fd_r, size_r, read_size, sum.blength);
@@ -231,11 +245,18 @@
 			stats.literal_data += i;
 			cleanup_got_literal = 1;
 
-			sum_update(data, i);
+			if (f_in >= 0)
+				/* no need for md4 during fast copy -goldor */
+				sum_update(data, i);
 
 			if (fd != -1 && write_file(fd,data,i) != i)
 				goto report_write_error;
 			offset += i;
+			if (f_in < 0 && percent < offset * 100 / total_size) {
+				/* report progress of fast copy to sender every 1% -goldor */
+				write_buf(local_socket, (char *)&offset, sizeof(offset));
+				percent = offset * 100 / total_size;
+			}
 			continue;
 		}
 
@@ -307,6 +328,12 @@
 	if (mapbuf)
 		unmap_file(mapbuf);
 
+	if (f_in < 0) {
+		/* close fast copy handle -goldor */
+		close(-f_in);
+		return 1;
+	}
+
 	read_buf(f_in, file_sum2, sum_len);
 	if (verbose > 2)
 		rprintf(FINFO,"got file_sum\n");
diff -au rsync-3.0.6/sender.c rsync-3.0.6.fast/sender.c
--- rsync-3.0.6/sender.c	2009-04-12 22:48:59.000000000 +0300
+++ rsync-3.0.6.fast/sender.c	2009-09-23 22:24:59.000000000 +0300
@@ -35,6 +35,8 @@
 extern int allowed_lull;
 extern int preserve_xattrs;
 extern int protocol_version;
+extern int local_server;
+extern int local_socket;
 extern int remove_source_files;
 extern int updating_basis_file;
 extern int make_backups;
@@ -319,6 +321,16 @@
 				path,slash,fname, (double)st.st_size);
 		}
 
+		/* send_fd for fast copy before sending sum head, 
+		 * which will trigger the recv_fd at the receiver -goldor */
+		if (local_server && local_socket && s->count == 0) {
+			if (!mbuf)
+				/* easy fix for fast copy of 0 length files -goldor */
+				mbuf = map_file(fd, 0, 0, s->blength);
+			rprintf(FINFO,"sending file descriptor %d\n", mbuf->fd);
+			send_fd(local_socket, mbuf->fd);
+		}
+
 		write_ndx_and_attrs(f_out, ndx, iflags, fname, file,
 				    fnamecmp_type, xname, xlen);
 		write_sum_head(f_xfer, s);
@@ -333,7 +345,18 @@
 
 		set_compression(fname);
 
-		match_sums(f_xfer, s, mbuf, st.st_size);
+		if (local_server && local_socket && s->count == 0) {
+			OFF_T offset = 0;
+			while (offset < st.st_size) {
+				read_buf(local_socket, (char *)&offset, sizeof(offset));
+				if (offset < 0)
+					break;
+				if (do_progress)
+					show_progress(offset, st.st_size);
+			}
+		}
+		else
+			match_sums(f_xfer, s, mbuf, st.st_size);
 		if (do_progress)
 			end_progress(st.st_size);
 
diff -au rsync-3.0.6/token.c rsync-3.0.6.fast/token.c
--- rsync-3.0.6/token.c	2009-01-17 23:41:35.000000000 +0200
+++ rsync-3.0.6.fast/token.c	2009-09-23 13:33:17.000000000 +0300
@@ -212,7 +212,7 @@
 /* non-compressing recv token */
 static int32 simple_recv_token(int f, char **data)
 {
-	static int32 residue;
+	static off_t residue;
 	static char *buf;
 	int32 n;
 
@@ -222,6 +222,21 @@
 			out_of_memory("simple_recv_token");
 	}
 
+	if (f < 0) {
+		/* fast copy handle */
+		f = -f;
+		if (!*data) {
+			/* start reading this file */
+			struct stat stbuf;
+			if (fstat(f, &stbuf) < 0)
+				return -1;
+			residue = stbuf.st_size;
+			*data = buf;
+		}
+		if (residue == 0)
+			return 0;
+	}
+
 	if (residue == 0) {
 		int32 i = read_int(f);
 		if (i <= 0)


More information about the rsync mailing list