This patch adds the --clone-dest option that works link --link-dest
but without requiring the metadata of the files to match in order
to be able to share the file's data.

NOTE: this patch is mostly untested because I don't currently have
a btrfs mount to test it out on.  I still need to make sure that a
cloned file gets its destination attributes set correctly after the
clone, for instance.

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

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

based-on: 0ac7ebceef70417355f25daf9e2fd94e84c49749
diff --git a/Makefile.in b/Makefile.in
--- a/Makefile.in
+++ b/Makefile.in
@@ -53,7 +53,7 @@ popt_OBJS=popt/findme.o  popt/popt.o  popt/poptconfig.o \
 	popt/popthelp.o popt/poptparse.o
 OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @BUILD_POPT@
 
-TLS_OBJ = tls.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
+TLS_OBJ = tls.o syscall.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o lib/wildmatch.o @BUILD_POPT@
 
 # Programs we must have to run the test cases
 CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \
@@ -168,7 +168,7 @@ getgroups$(EXEEXT): getgroups.o
 getfsdev$(EXEEXT): getfsdev.o
 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS)
 
-TRIMSLASH_OBJ = trimslash.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o
+TRIMSLASH_OBJ = trimslash.o syscall.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o
 trimslash$(EXEEXT): $(TRIMSLASH_OBJ)
 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS)
 
diff --git a/generator.c b/generator.c
--- a/generator.c
+++ b/generator.c
@@ -959,7 +959,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
 			best_match = j;
 			match_level = 2;
 		}
-		if (unchanged_attrs(cmpbuf, file, sxp)) {
+		if (alt_dest_type == CLONE_DEST || unchanged_attrs(cmpbuf, file, sxp)) {
 			best_match = j;
 			match_level = 3;
 			break;
@@ -985,9 +985,16 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
 				goto got_nothing_for_ya;
 		}
 #ifdef SUPPORT_HARD_LINKS
-		if (alt_dest_type == LINK_DEST) {
-			if (!hard_link_one(file, fname, cmpbuf, 1))
-				goto try_a_copy;
+		if (alt_dest_type == LINK_DEST || alt_dest_type == CLONE_DEST) {
+			if (alt_dest_type == LINK_DEST) {
+				if (!hard_link_one(file, fname, cmpbuf, 1))
+					goto try_a_copy;
+			} else if (do_clone(cmpbuf, fname, file->mode) == 0) {
+				finish_transfer(fname, fname, cmpbuf, NULL, file, 1, 0);
+			} else {
+				rsyserr(FERROR_XFER, errno, "failed to clone %s to %s", cmpbuf, fname);
+				exit_cleanup(RERR_UNSUPPORTED);
+			}
 			if (atimes_ndx)
 				set_file_attrs(fname, file, sxp, NULL, 0);
 			if (preserve_hard_links && F_IS_HLINKED(file))
@@ -1101,7 +1108,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
 
 	if (match_level == 3) {
 #ifdef SUPPORT_HARD_LINKS
-		if (alt_dest_type == LINK_DEST
+		if ((alt_dest_type == LINK_DEST || alt_dest_type == CLONE_DEST)
 #ifndef CAN_HARDLINK_SYMLINK
 		 && !S_ISLNK(file->mode)
 #endif
diff --git a/options.c b/options.c
--- a/options.c
+++ b/options.c
@@ -578,7 +578,7 @@ enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
       OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD,
       OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
       OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_BLOCK_SIZE,
-      OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR,
+      OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR, OPT_CLONE_DEST,
       OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, OPT_OLD_ARGS,
       OPT_STOP_AFTER, OPT_STOP_AT,
       OPT_REFUSED_BASE = 9000};
@@ -739,6 +739,7 @@ static struct poptOption long_options[] = {
   {"compare-dest",     0,  POPT_ARG_STRING, 0, OPT_COMPARE_DEST, 0, 0 },
   {"copy-dest",        0,  POPT_ARG_STRING, 0, OPT_COPY_DEST, 0, 0 },
   {"link-dest",        0,  POPT_ARG_STRING, 0, OPT_LINK_DEST, 0, 0 },
+  {"clone-dest",       0,  POPT_ARG_STRING, 0, OPT_CLONE_DEST, 0, 0 },
   {"fuzzy",           'y', POPT_ARG_NONE,   0, 'y', 0, 0 },
   {"no-fuzzy",         0,  POPT_ARG_VAL,    &fuzzy_basis, 0, 0, 0 },
   {"no-y",             0,  POPT_ARG_VAL,    &fuzzy_basis, 0, 0, 0 },
@@ -997,6 +998,9 @@ static void set_refuse_options(void)
 #ifndef SUPPORT_HARD_LINKS
 	parse_one_refuse_match(0, "link-dest", list_end);
 #endif
+#ifndef FICLONE
+	parse_one_refuse_match(0, "clone-dest", list_end);
+#endif
 #ifndef HAVE_MKTIME
 	parse_one_refuse_match(0, "stop-at", list_end);
 #endif
@@ -1326,6 +1330,8 @@ char *alt_dest_opt(int type)
 		return "--copy-dest";
 	case LINK_DEST:
 		return "--link-dest";
+	case CLONE_DEST:
+		return "--clone-dest";
 	default:
 		NOISY_DEATH("Unknown alt_dest_opt type");
 	}
@@ -1703,6 +1709,10 @@ int parse_arguments(int *argc_p, const char ***argv_p)
 			want_dest_type = LINK_DEST;
 			goto set_dest_dir;
 
+		case OPT_CLONE_DEST:
+			want_dest_type = CLONE_DEST;
+			goto set_dest_dir;
+
 		case OPT_COPY_DEST:
 			want_dest_type = COPY_DEST;
 			goto set_dest_dir;
diff --git a/rsync.1.md b/rsync.1.md
--- a/rsync.1.md
+++ b/rsync.1.md
@@ -430,6 +430,7 @@ has its own detailed description later in this manpage.
 --compare-dest=DIR       also compare destination files relative to DIR
 --copy-dest=DIR          ... and include copies of unchanged files
 --link-dest=DIR          hardlink to files in DIR when unchanged
+--clone-dest=DIR         clone (reflink) files from DIR when unchanged
 --compress, -z           compress file data during the transfer
 --compress-choice=STR    choose the compression algorithm (aka --zc)
 --compress-level=NUM     explicitly set compression level (aka --zl)
@@ -2532,6 +2533,18 @@ your home directory (remove the '=' for that).
     this bug by avoiding the `-o` option (or using `--no-o`) when sending to an
     old rsync.
 
+0.  `--clone-dest=DIR`
+
+    This option behaves like [`--link-dest`](#opt), but unchanged files are
+    reflinked from _DIR_ to the destination directory.  The files do not need
+    to match in attributes, as the data is cloned separately from the
+    attributes.
+
+    If _DIR_ is a relative path, it is relative to the destination directory.
+    See also [`--compare-dest`](#opt) and [`--copy-dest`](#opt).
+
+    All non-regular files are hard-linked (when possible).
+
 0.  `--compress`, `-z`
 
     With this option, rsync compresses the file data as it is sent to the
diff --git a/rsync.h b/rsync.h
--- a/rsync.h
+++ b/rsync.h
@@ -175,6 +175,11 @@
 #define COMPARE_DEST 1
 #define COPY_DEST 2
 #define LINK_DEST 3
+#define CLONE_DEST 4
+
+#if !defined FICLONE && defined __linux__
+#define FICLONE _IOW(0x94, 9, int)
+#endif
 
 #define MPLEX_BASE 7
 
diff --git a/syscall.c b/syscall.c
--- a/syscall.c
+++ b/syscall.c
@@ -146,6 +146,54 @@ int do_link(const char *old_path, const char *new_path)
 }
 #endif
 
+int do_clone(const char *old_path, const char *new_path, mode_t mode)
+{
+#ifdef FICLONE
+	int ifd, ofd, ret, save_errno;
+
+	if (dry_run) return 0;
+	RETURN_ERROR_IF_RO_OR_LO;
+
+	if ((ifd = do_open(old_path, O_RDONLY, 0)) < 0) {
+		save_errno = errno;
+		rsyserr(FERROR_XFER, errno, "open %s", full_fname(old_path));
+		errno = save_errno;
+		return -1;
+	}
+
+	if (robust_unlink(new_path) && errno != ENOENT) {
+		save_errno = errno;
+		rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(new_path));
+		close(ifd);
+		errno = save_errno;
+		return -1;
+	}
+
+	mode &= INITACCESSPERMS;
+	if ((ofd = do_open(new_path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0) {
+		save_errno = errno;
+		rsyserr(FERROR_XFER, save_errno, "open %s", full_fname(new_path));
+		close(ifd);
+		errno = save_errno;
+		return -1;
+	}
+
+	ret = ioctl(ofd, FICLONE, ifd);
+	save_errno = errno;
+	close(ifd);
+	close(ofd);
+	if (ret < 0)
+		unlink(new_path);
+	errno = save_errno;
+	return ret;
+#else
+	(void)old_path;
+	(void)new_path;
+	errno = ENOTSUP;
+	return -1;
+#endif
+}
+
 int do_lchown(const char *path, uid_t owner, gid_t group)
 {
 	if (dry_run) return 0;
diff --git a/t_stub.c b/t_stub.c
--- a/t_stub.c
+++ b/t_stub.c
@@ -39,6 +39,7 @@ size_t max_alloc = 0; /* max_alloc is needed when combined with util2.o */
 char *partial_dir;
 char *module_dir;
 filter_rule_list daemon_filter_list;
+short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG];
 
  void rprintf(UNUSED(enum logcode code), const char *format, ...)
 {
diff --git a/t_unsafe.c b/t_unsafe.c
--- a/t_unsafe.c
+++ b/t_unsafe.c
@@ -28,7 +28,6 @@ int am_root = 0;
 int am_sender = 1;
 int read_only = 0;
 int list_only = 0;
-short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG];
 
 int
 main(int argc, char **argv)
diff -Nurp a/rsync.1 b/rsync.1
--- a/rsync.1
+++ b/rsync.1
@@ -507,6 +507,7 @@ has its own detailed description later i
 --compare-dest=DIR       also compare destination files relative to DIR
 --copy-dest=DIR          ... and include copies of unchanged files
 --link-dest=DIR          hardlink to files in DIR when unchanged
+--clone-dest=DIR         clone (reflink) files from DIR when unchanged
 --compress, -z           compress file data during the transfer
 --compress-choice=STR    choose the compression algorithm (aka --zc)
 --compress-level=NUM     explicitly set compression level (aka --zl)
@@ -2575,6 +2576,16 @@ Note that rsync versions prior to 2.6.1
 \fB\-\-owner\fP (\fB\-o\fP) was specified (or implied).  You can work-around
 this bug by avoiding the \fB\-o\fP option (or using \fB\-\-no-o\fP) when sending to an
 old rsync.
+.IP "\fB\-\-clone-dest=DIR\fP"
+This option behaves like \fB\-\-link-dest\fP, but unchanged files are
+reflinked from \fIDIR\fP to the destination directory.  The files do not need
+to match in attributes, as the data is cloned separately from the
+attributes.
+.IP
+If \fIDIR\fP is a relative path, it is relative to the destination directory.
+See also \fB\-\-compare-dest\fP and \fB\-\-copy-dest\fP.
+.IP
+All non-regular files are hard-linked (when possible).
 .IP "\fB\-\-compress\fP, \fB\-z\fP"
 With this option, rsync compresses the file data as it is sent to the
 destination machine, which reduces the amount of data being transmitted\ \-\-
diff -Nurp a/rsync.1.html b/rsync.1.html
--- a/rsync.1.html
+++ b/rsync.1.html
@@ -425,6 +425,7 @@ has its own detailed description later i
 --compare-dest=DIR       also compare destination files relative to DIR
 --copy-dest=DIR          ... and include copies of unchanged files
 --link-dest=DIR          hardlink to files in DIR when unchanged
+--clone-dest=DIR         clone (reflink) files from DIR when unchanged
 --compress, -z           compress file data during the transfer
 --compress-choice=STR    choose the compression algorithm (aka --zc)
 --compress-level=NUM     explicitly set compression level (aka --zl)
@@ -2382,6 +2383,16 @@ this bug by avoiding the <code>-o</code>
 old rsync.</p>
 </dd>
 
+<dt id="opt--clone-dest"><code>--clone-dest=DIR</code><a href="#opt--clone-dest" class="tgt"></a></dt><dd>
+<p>This option behaves like <a href="#opt--link-dest"><code>--link-dest</code></a>, but unchanged files are
+reflinked from <u>DIR</u> to the destination directory.  The files do not need
+to match in attributes, as the data is cloned separately from the
+attributes.</p>
+<p>If <u>DIR</u> is a relative path, it is relative to the destination directory.
+See also <a href="#opt--compare-dest"><code>--compare-dest</code></a> and <a href="#opt--copy-dest"><code>--copy-dest</code></a>.</p>
+<p>All non-regular files are hard-linked (when possible).</p>
+</dd>
+
 <span id="opt-z"></span><dt id="opt--compress"><code>--compress</code>, <code>-z</code><a href="#opt--compress" class="tgt"></a></dt><dd>
 <p>With this option, rsync compresses the file data as it is sent to the
 destination machine, which reduces the amount of data being transmitted&nbsp;-&#8288;-&#8288;
