--- backup.c	10 Jun 2005 17:57:18 -0000	1.45
+++ backup.c	15 Sep 2005 18:22:11 -0000
@@ -52,43 +52,6 @@ char *get_backup_name(char *fname)
 	return NULL;
 }
 
-/* simple backup creates a backup with a suffix in the same directory */
-static int make_simple_backup(char *fname)
-{
-	int rename_errno;
-	char *fnamebak = get_backup_name(fname);
-
-	if (!fnamebak)
-		return 0;
-
-	while (1) {
-		if (do_rename(fname, fnamebak) == 0) {
-			if (verbose > 1) {
-				rprintf(FINFO, "backed up %s to %s\n",
-					safe_fname(fname),
-					safe_fname(fnamebak));
-			}
-			break;
-		}
-		/* cygwin (at least version b19) reports EINVAL */
-		if (errno == ENOENT || errno == EINVAL)
-			break;
-
-		rename_errno = errno;
-		if (errno == EISDIR && do_rmdir(fnamebak) == 0)
-			continue;
-		if (errno == ENOTDIR && do_unlink(fnamebak) == 0)
-			continue;
-
-		rsyserr(FERROR, rename_errno, "rename %s to backup %s",
-			safe_fname(fname), safe_fname(fnamebak));
-		errno = rename_errno;
-		return 0;
-	}
-
-	return 1;
-}
-
 
 /****************************************************************************
 Create a directory given an absolute path, perms based upon another directory
@@ -157,19 +120,83 @@ failure:
 	return -1;
 }
 
-/* robustly move a file, creating new directory structures if necessary */
-static int robust_move(char *src, char *dst)
+/* This might fall-back to rename() if link() doesn't work. */
+static int do_backup(char *src, char *dst, int try_hlink, int nlinks)
 {
-	if (robust_rename(src, dst, 0755) < 0 && (errno != ENOENT
-	    || make_bak_dir(dst) < 0 || robust_rename(src, dst, 0755) < 0))
+	int backup_errno;
+
+#ifndef SUPPORT_LINKS
+	backup_errno = try_hlink; /* Use an unused var... */
+#endif
+
+	while (1) {
+#ifdef SUPPORT_LINKS
+		if (try_hlink) {
+			robust_unlink(dst);
+			if (do_link(src, dst) == 0)
+				break;
+			if (errno == EPERM) {
+				try_hlink = 0;
+				continue;
+			}
+
+			backup_errno = errno;
+
+			if (errno == EXDEV && copy_file(src, dst, 0600) == 0)
+				break;
+		} else
+#endif
+		{
+			if (robust_rename(src, dst, 0600) == 0) {
+				/* If file is hard-linked into backup dir,
+				 * rename() might succeed but do nothing! */
+				if (nlinks > 1)
+					robust_unlink(src); /* Just in case... */
+				break;
+			}
+
+			backup_errno = errno;
+		}
+
+		if (backup_dir && backup_errno == ENOENT
+		 && make_bak_dir(dst) == 0)
+			continue;
+
+		/* cygwin (at least version b19) reports EINVAL */
+		if (backup_errno == ENOENT || backup_errno == EINVAL)
+			break;
+
+		if (backup_errno == EISDIR && do_rmdir(src) == 0)
+			continue;
+		if (backup_errno == ENOTDIR && do_unlink(src) == 0)
+			continue;
+
+		rsyserr(FERROR, backup_errno, "backup of %s to %s",
+			safe_fname(src), safe_fname(dst));
+		errno = backup_errno;
 		return -1;
+	}
+
+	if (verbose > 1) {
+		rprintf(FINFO, "backed up %s to %s\n",
+			safe_fname(src), safe_fname(dst));
+	}
 	return 0;
 }
 
+/* simple backup creates a backup with a suffix in the same directory */
+static int make_simple_backup(char *fname, int try_hlink)
+{
+	char *fnamebak = get_backup_name(fname);
+
+	if (fnamebak && do_backup(fname, fnamebak, try_hlink, 0) == 0)
+		return 1;
+	return 0;
+}
 
 /* If we have a --backup-dir, then we get here from make_backup().
- * We will move the file to be deleted into a parallel directory tree. */
-static int keep_backup(char *fname)
+ * We will move/link the file into a parallel directory tree. */
+static int keep_backup(char *fname, int try_hlink)
 {
 	STRUCT_STAT st;
 	struct file_struct *file;
@@ -199,8 +226,9 @@ static int keep_backup(char *fname)
 			rprintf(FINFO, "make_backup: DEVICE %s successful.\n",
 				safe_fname(fname));
 		}
+		if (!try_hlink)
+			do_unlink(fname);
 		kept = 1;
-		do_unlink(fname);
 	}
 
 	if (!kept && S_ISDIR(file->mode)) {
@@ -212,6 +240,7 @@ static int keep_backup(char *fname)
 				full_fname(buf));
 		}
 
+		/* Note: try_hlink must be 0 when handling a dir. */
 		ret_code = do_rmdir(fname);
 		if (verbose > 2) {
 			rprintf(FINFO, "make_backup: RMDIR %s returns %i\n",
@@ -237,7 +266,8 @@ static int keep_backup(char *fname)
 					full_fname(buf),
 					safe_fname(file->u.link));
 			}
-			do_unlink(fname);
+			if (!try_hlink)
+				do_unlink(fname);
 			kept = 1;
 		}
 	}
@@ -249,32 +279,20 @@ static int keep_backup(char *fname)
 		return 1;
 	}
 
-	/* move to keep tree if a file */
-	if (!kept) {
-		if (robust_move(fname, buf) != 0) {
-			rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"",
-				full_fname(fname), safe_fname(buf));
-		} else if (st.st_nlink > 1) {
-			/* If someone has hard-linked the file into the backup
-			 * dir, rename() might return success but do nothing! */
-			robust_unlink(fname); /* Just in case... */
-		}
-	}
+	/* If a normal file, make a backup. */
+	if (!kept)
+		do_backup(fname, buf, try_hlink, st.st_nlink);
 	set_perms(buf, file, NULL, 0);
 	free(file);
 
-	if (verbose > 1) {
-		rprintf(FINFO, "backed up %s to %s\n",
-			safe_fname(fname), safe_fname(buf));
-	}
 	return 1;
 }
 
 
-/* main backup switch routine */
-int make_backup(char *fname)
+/* Main backup switch routine.  Note that try_hlink must be 0 for a dir. */
+int make_backup(char *fname, int try_hlink)
 {
 	if (backup_dir)
-		return keep_backup(fname);
-	return make_simple_backup(fname);
+		return keep_backup(fname, try_hlink);
+	return make_simple_backup(fname, try_hlink);
 }
--- generator.c	6 Sep 2005 18:12:38 -0000	1.222
+++ generator.c	15 Sep 2005 18:22:11 -0000
@@ -114,7 +114,7 @@ static int delete_item(char *fname, int 
 		if (max_delete && ++deletion_count > max_delete)
 			return 0;
 		if (make_backups && (backup_dir || !is_backup_file(fname)))
-			ok = make_backup(fname);
+			ok = make_backup(fname, 0);
 		else
 			ok = robust_unlink(fname) == 0;
 		if (ok) {
@@ -139,7 +139,7 @@ static int delete_item(char *fname, int 
 		errno = ENOTEMPTY;
 	} else if (make_backups && !backup_dir && !is_backup_file(fname)
 	    && !(flags & DEL_FORCE_RECURSE))
-		ok = make_backup(fname);
+		ok = make_backup(fname, 0);
 	else
 		ok = do_rmdir(fname) == 0;
 	if (ok) {
--- hlink.c	31 Jul 2005 23:19:42 -0000	1.54
+++ hlink.c	15 Sep 2005 18:22:11 -0000
@@ -154,7 +154,7 @@ static int maybe_hard_link(struct file_s
 			return 0;
 		}
 		if (make_backups) {
-			if (!make_backup(fname))
+			if (!make_backup(fname, 0))
 				return -1;
 		} else if (robust_unlink(fname)) {
 			rsyserr(FERROR, errno, "unlink %s failed",
--- receiver.c	30 Aug 2005 02:58:42 -0000	1.164
+++ receiver.c	15 Sep 2005 18:22:12 -0000
@@ -347,7 +347,7 @@ static void handle_delayed_updates(struc
 		struct file_struct *file = flist->files[i];
 		fname = local_name ? local_name : f_name(file);
 		if ((partialptr = partial_dir_fname(fname)) != NULL) {
-			if (make_backups && !make_backup(fname))
+			if (make_backups && !make_backup(fname, 1))
 				continue;
 			if (verbose > 2) {
 				rprintf(FINFO, "renaming %s to %s\n",
--- rsync.c	27 Jul 2005 23:30:58 -0000	1.168
+++ rsync.c	15 Sep 2005 18:22:13 -0000
@@ -179,7 +179,7 @@ void finish_transfer(char *fname, char *
 		goto do_set_perms;
 	}
 
-	if (make_backups && overwriting_basis && !make_backup(fname))
+	if (make_backups && overwriting_basis && !make_backup(fname, 1))
 		return;
 
 	/* Change permissions before putting the file into place. */
--- t_stub.c	25 Jan 2005 10:39:14 -0000	1.10
+++ t_stub.c	15 Sep 2005 18:22:13 -0000
@@ -28,6 +28,7 @@
 
 int modify_window = 0;
 int module_id = -1;
+int make_backups = 0;
 char *partial_dir;
 struct filter_list_struct server_filter_list;
 
--- util.c	3 Aug 2005 04:51:29 -0000	1.186
+++ util.c	15 Sep 2005 18:22:14 -0000
@@ -31,6 +31,7 @@ extern int verbose;
 extern int dry_run;
 extern int module_id;
 extern int modify_window;
+extern int make_backups;
 extern char *partial_dir;
 extern struct filter_list_struct server_filter_list;
 
@@ -392,6 +393,12 @@ int robust_rename(char *from, char *to, 
 			break;
 #endif
 		case EXDEV:
+#ifdef SUPPORT_LINKS
+			if (make_backups && robust_unlink(to) != 0
+			 && errno != ENOENT)
+				return -1;
+#endif
+
 			if (copy_file(from, to, mode) != 0)
 				return -2;
 			do_unlink(from);
