diff -Naur orig/generator.c new/generator.c --- orig/generator.c 2009-04-26 16:51:50.000000000 +0200 +++ new/generator.c 2009-09-29 20:30:52.000000000 +0200 @@ -21,6 +21,7 @@ */ #include "rsync.h" +#include "same-inode.h" extern int verbose; extern int dry_run; @@ -977,6 +978,8 @@ int best_match = -1; int match_level = 0; int j = 0; + STRUCT_STAT st_tmp; + int statres, stat_err; do { pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname); @@ -1016,8 +1019,25 @@ if (match_level == 3 && !copy_dest) { #ifdef SUPPORT_HARD_LINKS if (link_dest) { - if (!hard_link_one(file, fname, cmpbuf, 1)) - goto try_a_copy; + statres = do_stat(fname, &st_tmp); + stat_err = errno; + /* if we have no error getting file info, or the error is that it + * doesn't exist... */ + if (!statres || stat_err == ENOENT) { + if (!SAME_INODE(sxp->st, st_tmp)) { + if (!statres) { + rprintf(FCLIENT, "%s => %s\n", fname, cmpbuf); + } + if (!hard_link_one(file, fname, cmpbuf, 1)) { + goto try_a_copy; + } + } + } + else { + rprintf(FERROR, + "internal: failed call to do_stat(%s, &old_st) returned code %d (0x%x). errno=%d(0x%x)\n", + cmpbuf, statres, statres, stat_err, stat_err); + } if (preserve_hard_links && F_IS_HLINKED(file)) finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, j); if (!maybe_ATTRS_REPORT && (verbose > 1 || stdout_format_has_i > 1)) { @@ -1038,6 +1058,22 @@ #ifdef SUPPORT_HARD_LINKS try_a_copy: /* Copy the file locally. */ #endif + /* NEW SCENARIO: if we're here, we have a link-dest that doesn't + * match exactly, at least not with file attributes. + * It would be unwise to replace fname (the dest) with a hard + * link to link-dest if our link-dest doesn't match exactly. + * TODO: consider this: + if (!unchanged_file(fname, file, &sxp->st)) + replace_fname_with_link-dest_hlink + else + return -1; + * Basically, if we're sure that we're going to throw away fname + * (the dest file) anyway, might as well replace it with a hard link + * to our link dest, and see how it goes. */ + if (link_stat(fname, &st_tmp, 0) >= 0) { + return -1; + } + if (!dry_run && copy_altdest_file(cmpbuf, fname, file) < 0) return -1; if (itemizing) @@ -1740,18 +1776,27 @@ stat_errno = ENOENT; } - if (statret != 0 && basis_dir[0] != NULL) { - int j = try_dests_reg(file, fname, ndx, fnamecmpbuf, &sx, + if (basis_dir[0] != NULL) { + // temporarily abuse real_sx; we're resetting it in a moment + int j = try_dests_reg(file, fname, ndx, fnamecmpbuf, &real_sx, itemizing, code); if (j == -2) { + /* sx is probably not used later, but for completeness, let's be sure + * that we've restored this */ + sx = real_sx; if (remove_source_files == 1) goto return_with_success; goto cleanup; } if (j >= 0) { - fnamecmp = fnamecmpbuf; - fnamecmp_type = j; - statret = 0; + /* Only allow further comparisons with a link-dest file if + * the normal target file does not exist. */ + if (statret != 0) { + fnamecmp = fnamecmpbuf; + fnamecmp_type = j; + statret = 0; + sx = real_sx; + } } } diff -Naur orig/readme_dest_link_patch.txt new/readme_dest_link_patch.txt --- orig/readme_dest_link_patch.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/readme_dest_link_patch.txt 2009-09-29 19:28:29.000000000 +0200 @@ -0,0 +1,33 @@ +# 20090927 +# Bryant Hansen + +/* + * Rsync patch for rsync 3.0.6 for updating dest files from link-dest + * + * Fixes functionality that existing files in dest are never update + * from link-dest + * + * ALPHA VERSION!!! + * + * Mostly untested. Great big warning! Use at your own risk. + * + * Copyright (C) 2009 Bryant Hansen + * + * Totally dependent on the great work & copyrights of Andrew Tridgell, + * Wayne Davison and the other rsync copyright holders + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, visit the http://fsf.org website. + */ + + diff -Naur orig/same-inode.h new/same-inode.h --- orig/same-inode.h 1970-01-01 01:00:00.000000000 +0100 +++ new/same-inode.h 2009-05-04 07:46:33.000000000 +0200 @@ -0,0 +1,25 @@ +/* Determine whether two stat buffers refer to the same file. + + Copyright (C) 2006 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef SAME_INODE_H +# define SAME_INODE_H 1 + +# define SAME_INODE(Stat_buf_1, Stat_buf_2) \ + ((Stat_buf_1).st_ino == (Stat_buf_2).st_ino \ + && (Stat_buf_1).st_dev == (Stat_buf_2).st_dev) + +#endif diff -Naur orig/syscall.c new/syscall.c --- orig/syscall.c 2009-01-17 22:41:35.000000000 +0100 +++ new/syscall.c 2009-09-29 19:16:32.000000000 +0200 @@ -63,9 +63,18 @@ #ifdef HAVE_LINK int do_link(const char *fname1, const char *fname2) { + int link_result; if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; - return link(fname1, fname2); + + /* code borrowed from ln.c in coreutils */ + link_result = link(fname1, fname2); + if (link_result && errno == EEXIST) { + if (unlink (fname2) != 0) + return errno; + link_result = link(fname1, fname2); + } + return link_result; } #endif