This patch adds the --direct-io option, which opens files with O_DIRECT.

TODO: we probably need to make our I/O aligned on 512-byte boundaries.

Written by: Dag Wieers

To use this patch, run these commands for a successful build:

    patch -p1 <patches/direct-io.diff
    ./configure                         (optional if already run)
    make

based-on: 0ac7ebceef70417355f25daf9e2fd94e84c49749
diff --git a/options.c b/options.c
--- a/options.c
+++ b/options.c
@@ -24,6 +24,7 @@
 #include "ifuncs.h"
 #include <popt.h>
 
+extern int direct_io;
 extern int module_id;
 extern int local_server;
 extern int sanitize_paths;
@@ -760,6 +761,8 @@ static struct poptOption long_options[] = {
   {"partial-dir",      0,  POPT_ARG_STRING, &partial_dir, 0, 0, 0 },
   {"delay-updates",    0,  POPT_ARG_VAL,    &delay_updates, 1, 0, 0 },
   {"no-delay-updates", 0,  POPT_ARG_VAL,    &delay_updates, 0, 0, 0 },
+  {"direct-io",       'n', POPT_ARG_VAL,    &direct_io, 1, 0, 0 },
+  {"no-direct-io",     0,  POPT_ARG_VAL,    &direct_io, 0, 0, 0 },
   {"prune-empty-dirs",'m', POPT_ARG_VAL,    &prune_empty_dirs, 1, 0, 0 },
   {"no-prune-empty-dirs",0,POPT_ARG_VAL,    &prune_empty_dirs, 0, 0, 0 },
   {"no-m",             0,  POPT_ARG_VAL,    &prune_empty_dirs, 0, 0, 0 },
diff --git a/rsync.1.md b/rsync.1.md
--- a/rsync.1.md
+++ b/rsync.1.md
@@ -415,6 +415,7 @@ has its own detailed description later in this manpage.
 --partial                keep partially transferred files
 --partial-dir=DIR        put a partially transferred file into DIR
 --delay-updates          put all updated files into place at end
+--direct-io              don't use buffer cache for xfer file I/O
 --prune-empty-dirs, -m   prune empty directory chains from file-list
 --numeric-ids            don't map uid/gid values by user/group name
 --usermap=STRING         custom username mapping
@@ -3247,6 +3248,17 @@ your home directory (remove the '=' for that).
     update algorithm that is even more atomic (it uses [`--link-dest`](#opt)
     and a parallel hierarchy of files).
 
+0.  `--direct-io`
+
+    This option opens files with a direct-I/O flag that makes the file I/O
+    avoid the buffer cache.  The option only affects one side of the transfer
+    (unless the transfer is local).  If you want it to affect both sides, use
+    the [`--remote-option`](#opt) (`-M`) option to specify it for the remote
+    side.  For instance, this specifies it for both sides (via brace
+    expansion):
+
+    >     rsync -av {,-M}--direct-io /src/ host:/dest/
+
 0.  `--prune-empty-dirs`, `-m`
 
     This option tells the receiving rsync to get rid of empty directories from
diff --git a/syscall.c b/syscall.c
--- a/syscall.c
+++ b/syscall.c
@@ -44,6 +44,8 @@ extern int preserve_perms;
 extern int preserve_executability;
 extern int open_noatime;
 
+int direct_io = 0;
+
 #ifndef S_BLKSIZE
 # if defined hpux || defined __hpux__ || defined __hpux
 #  define S_BLKSIZE 1024
@@ -95,7 +97,12 @@ int do_symlink(const char *lnk, const char *path)
 	 * and write the lnk into it. */
 	if (am_root < 0) {
 		int ok, len = strlen(lnk);
-		int fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR);
+		int flags = O_WRONLY|O_CREAT|O_TRUNC;
+
+		if (direct_io)
+			flags |= O_DIRECT;
+
+		int fd = open(path, flags, S_IWUSR|S_IRUSR);
 		if (fd < 0)
 			return -1;
 		ok = write(fd, lnk, len) == len;
@@ -224,6 +231,8 @@ int do_open(const char *pathname, int flags, mode_t mode)
 	if (open_noatime)
 		flags |= O_NOATIME;
 #endif
+	if (direct_io)
+		flags |= O_DIRECT;
 
 	return open(pathname, flags | O_BINARY, mode);
 }
@@ -684,6 +693,9 @@ int do_open_nofollow(const char *pathname, int flags)
 #endif
 	}
 
+	if (direct_io)
+		flags |= O_DIRECT;
+
 #ifdef O_NOFOLLOW
 	fd = open(pathname, flags|O_NOFOLLOW);
 #else
diff -Nurp a/rsync.1 b/rsync.1
--- a/rsync.1
+++ b/rsync.1
@@ -492,6 +492,7 @@ has its own detailed description later i
 --partial                keep partially transferred files
 --partial-dir=DIR        put a partially transferred file into DIR
 --delay-updates          put all updated files into place at end
+--direct-io              don't use buffer cache for xfer file I/O
 --prune-empty-dirs, -m   prune empty directory chains from file-list
 --numeric-ids            don't map uid/gid values by user/group name
 --usermap=STRING         custom username mapping
@@ -3333,6 +3334,19 @@ will fail if they can't be renamed into
 See also the "atomic-rsync" python script in the "support" subdir for an
 update algorithm that is even more atomic (it uses \fB\-\-link-dest\fP
 and a parallel hierarchy of files).
+.IP "\fB\-\-direct-io\fP"
+This option opens files with a direct-I/O flag that makes the file I/O
+avoid the buffer cache.  The option only affects one side of the transfer
+(unless the transfer is local).  If you want it to affect both sides, use
+the \fB\-\-remote-option\fP (\fB\-M\fP) option to specify it for the remote
+side.  For instance, this specifies it for both sides (via brace
+expansion):
+.RS 4
+.IP
+.nf
+rsync -av {,-M}--direct-io /src/ host:/dest/
+.fi
+.RE
 .IP "\fB\-\-prune-empty-dirs\fP, \fB\-m\fP"
 This option tells the receiving rsync to get rid of empty directories from
 the file-list, including nested directories that have no non-directory
diff -Nurp a/rsync.1.html b/rsync.1.html
--- a/rsync.1.html
+++ b/rsync.1.html
@@ -410,6 +410,7 @@ has its own detailed description later i
 --partial                keep partially transferred files
 --partial-dir=DIR        put a partially transferred file into DIR
 --delay-updates          put all updated files into place at end
+--direct-io              don't use buffer cache for xfer file I/O
 --prune-empty-dirs, -m   prune empty directory chains from file-list
 --numeric-ids            don't map uid/gid values by user/group name
 --usermap=STRING         custom username mapping
@@ -3050,6 +3051,19 @@ update algorithm that is even more atomi
 and a parallel hierarchy of files).</p>
 </dd>
 
+<dt id="opt--direct-io"><code>--direct-io</code><a href="#opt--direct-io" class="tgt"></a></dt><dd>
+<p>This option opens files with a direct-I/O flag that makes the file I/O
+avoid the buffer cache.  The option only affects one side of the transfer
+(unless the transfer is local).  If you want it to affect both sides, use
+the <a href="#opt--remote-option"><code>--remote-option</code></a> (<code>-M</code>) option to specify it for the remote
+side.  For instance, this specifies it for both sides (via brace
+expansion):</p>
+<blockquote>
+<pre><code>rsync -av {,-M}--direct-io /src/ host:/dest/
+</code></pre>
+</blockquote>
+</dd>
+
 <span id="opt-m"></span><dt id="opt--prune-empty-dirs"><code>--prune-empty-dirs</code>, <code>-m</code><a href="#opt--prune-empty-dirs" class="tgt"></a></dt><dd>
 <p>This option tells the receiving rsync to get rid of empty directories from
 the file-list, including nested directories that have no non-directory
