Adding filter-attribute-mods patch; updating patches.
[rsync/rsync-patches.git] / link-by-hash.diff
index 49d2ad7..1415923 100644 (file)
@@ -1,30 +1,55 @@
-After applying this patch and running configure, you MUST run this
-command before "make":
-
-    make proto
-
-Jason M. Felice writes:
+Jason M. Felice wrote:
 
 This patch adds the --link-by-hash=DIR option, which hard links received
 files in a link farm arranged by MD4 file hash.  The result is that the system
 will only store one copy of the unique contents of each file, regardless of
 the file's name.
 
 
 This patch adds the --link-by-hash=DIR option, which hard links received
 files in a link farm arranged by MD4 file hash.  The result is that the system
 will only store one copy of the unique contents of each file, regardless of
 the file's name.
 
+To use this patch, run these commands for a successful build:
 
 
---- orig/Makefile.in   2004-07-04 08:59:17
-+++ Makefile.in        2004-07-03 20:20:15
-@@ -35,7 +35,7 @@ OBJS1=rsync.o generator.o receiver.o cle
-       main.o checksum.o match.o syscall.o log.o backup.o
- OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \
-       fileio.o batch.o clientname.o
+    patch -p1 <patches/link-by-hash.diff
+    ./prepare-source
+    ./configure
+    make
+
+based-on: 181c9faf928faad08ef095f4667afe460ec3bef6
+diff --git a/Makefile.in b/Makefile.in
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -37,7 +37,7 @@ OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
+       util.o main.o checksum.o match.o syscall.o log.o backup.o delete.o
+ OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
+       fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
 -OBJS3=progress.o pipe.o
 +OBJS3=progress.o pipe.o hashlink.o
  DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
  popt_OBJS=popt/findme.o  popt/popt.o  popt/poptconfig.o \
        popt/popthelp.o popt/poptparse.o
 -OBJS3=progress.o pipe.o
 +OBJS3=progress.o pipe.o hashlink.o
  DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
  popt_OBJS=popt/findme.o  popt/popt.o  popt/poptconfig.o \
        popt/popthelp.o popt/poptparse.o
---- orig/hashlink.c    2004-07-02 21:41:54
-+++ hashlink.c 2004-07-02 21:41:54
-@@ -0,0 +1,342 @@
+diff --git a/flist.c b/flist.c
+--- a/flist.c
++++ b/flist.c
+@@ -73,6 +73,7 @@ extern int sender_keeps_checksum;
+ extern int unsort_ndx;
+ extern struct stats stats;
+ extern char *filesfrom_host;
++extern char *link_by_hash_dir;
+ extern char *usermap, *groupmap;
+ extern char curr_dir[MAXPATHLEN];
+@@ -891,7 +892,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
+               extra_len += EXTRA_LEN;
+ #endif
+-      if (always_checksum && S_ISREG(mode))
++      if ((always_checksum || link_by_hash_dir) && S_ISREG(mode))
+               extra_len += SUM_EXTRA_CNT * EXTRA_LEN;
+ #if SIZEOF_INT64 >= 8
+diff --git a/hashlink.c b/hashlink.c
+new file mode 100644
+--- /dev/null
++++ b/hashlink.c
+@@ -0,0 +1,339 @@
 +/*
 +   Copyright (C) Cronosys, LLC 2004
 +
 +/*
 +   Copyright (C) Cronosys, LLC 2004
 +
@@ -51,14 +76,12 @@ the file's name.
 +
 +#ifdef HAVE_LINK
 +
 +
 +#ifdef HAVE_LINK
 +
-+charmake_hash_name(struct file_struct *file)
++char *make_hash_name(struct file_struct *file)
 +{
 +      char hash[33], *dst;
 +{
 +      char hash[33], *dst;
-+      unsigned char *src;
-+      unsigned char c;
++      uchar c, *src = (uchar*)F_SUM(file);
 +      int i;
 +
 +      int i;
 +
-+      src = (unsigned char*)file->u.sum;
 +      for (dst = hash, i = 0; i < 4; i++, src++) {
 +              c = *src >> 4;
 +              *(dst++) = (c >= 10) ? (c - 10 + 'a') : (c + '0');
 +      for (dst = hash, i = 0; i < 4; i++, src++) {
 +              c = *src >> 4;
 +              *(dst++) = (c >= 10) ? (c - 10 + 'a') : (c + '0');
@@ -74,7 +97,8 @@ the file's name.
 +      }
 +      *dst = 0;
 +
 +      }
 +      *dst = 0;
 +
-+      asprintf(&dst,"%s/%s",link_by_hash_dir,hash);
++      if (asprintf(&dst,"%s/%s",link_by_hash_dir,hash) < 0)
++              out_of_memory("make_hash_name");
 +      return dst;
 +}
 +
 +      return dst;
 +}
 +
@@ -111,11 +135,11 @@ the file's name.
 +      long this_fnbr;
 +
 +      *fnbr = 0;
 +      long this_fnbr;
 +
 +      *fnbr = 0;
-+      
++
 +      /* Build a list of potential candidates and open
 +       * them. */
 +      if ((d = opendir(hashname)) == NULL) {
 +      /* Build a list of potential candidates and open
 +       * them. */
 +      if ((d = opendir(hashname)) == NULL) {
-+              rsyserr(FERROR, errno, "opendir \"%s\"", hashname);
++              rsyserr(FERROR, errno, "opendir failed: \"%s\"", hashname);
 +              free(hashname);
 +              return NULL;
 +      }
 +              free(hashname);
 +              return NULL;
 +      }
@@ -130,11 +154,11 @@ the file's name.
 +              if (this_fnbr > *fnbr)
 +                      *fnbr = this_fnbr;
 +
 +              if (this_fnbr > *fnbr)
 +                      *fnbr = this_fnbr;
 +
-+              hashfile = (struct hashfile_struct*)malloc(sizeof(struct hashfile_struct));
-+              asprintf(&hashfile->name,"%s/%s",hashname,
-+                       di->d_name);
++              hashfile = new_array(struct hashfile_struct, 1);
++              if (asprintf(&hashfile->name,"%s/%s",hashname, di->d_name) < 0)
++                      out_of_memory("find_hashfiles");
 +              if (do_stat(hashfile->name,&st) == -1) {
 +              if (do_stat(hashfile->name,&st) == -1) {
-+                      rsyserr(FERROR, errno, "%s: %s", hashfile->name);
++                      rsyserr(FERROR, errno, "stat failed: %s", hashfile->name);
 +                      kill_hashfile(hashfile);
 +                      continue;
 +              }
 +                      kill_hashfile(hashfile);
 +                      continue;
 +              }
@@ -145,7 +169,7 @@ the file's name.
 +              hashfile->nlink = st.st_nlink;
 +              hashfile->fd = open(hashfile->name,O_RDONLY|O_BINARY);
 +              if (hashfile->fd == -1) {
 +              hashfile->nlink = st.st_nlink;
 +              hashfile->fd = open(hashfile->name,O_RDONLY|O_BINARY);
 +              if (hashfile->fd == -1) {
-+                      rsyserr(FERROR, errno, "%s", hashfile->name);
++                      rsyserr(FERROR, errno, "open failed: %s", hashfile->name);
 +                      kill_hashfile(hashfile);
 +                      continue;
 +              }
 +                      kill_hashfile(hashfile);
 +                      continue;
 +              }
@@ -210,11 +234,10 @@ the file's name.
 +                      /* There are no matches. */
 +                      return NULL;
 +              }
 +                      /* There are no matches. */
 +                      return NULL;
 +              }
-+              
 +      }
 +
 +      if (amt == -1) {
 +      }
 +
 +      if (amt == -1) {
-+              rsyserr(FERROR, errno, "%s");
++              rsyserr(FERROR, errno, "read failed in compare_hashfiles()");
 +              kill_hashfiles(files);
 +              return NULL;
 +      }
 +              kill_hashfiles(files);
 +              return NULL;
 +      }
@@ -247,17 +270,16 @@ the file's name.
 +}
 +
 +
 +}
 +
 +
-+int link_by_hash(char *fnametmp,char *fname,struct file_struct *file)
++int link_by_hash(const char *fnametmp, const char *fname, struct file_struct *file)
 +{
 +      STRUCT_STAT st;
 +{
 +      STRUCT_STAT st;
-+      char *hashname = make_hash_name(file);          
++      char *hashname = make_hash_name(file);
 +      int first = 0, rc;
 +      char *linkname;
 +      long last_fnbr;
 +
 +      int first = 0, rc;
 +      char *linkname;
 +      long last_fnbr;
 +
-+      if (file->length == 0) {
-+              return robust_rename(fnametmp,fname,0644);
-+      }
++      if (F_LENGTH(file) == 0)
++              return robust_rename(fnametmp, fname, NULL, 0644);
 +
 +      if (do_stat(hashname, &st) == -1) {
 +              char *dirname;
 +
 +      if (do_stat(hashname, &st) == -1) {
 +              char *dirname;
@@ -266,47 +288,48 @@ the file's name.
 +              dirname = strdup(hashname);
 +              *strrchr(dirname,'/') = 0;
 +              if (do_mkdir(dirname, 0755) == -1 && errno != EEXIST) {
 +              dirname = strdup(hashname);
 +              *strrchr(dirname,'/') = 0;
 +              if (do_mkdir(dirname, 0755) == -1 && errno != EEXIST) {
-+                      rsyserr(FERROR, errno, "mkdir %s", dirname);
++                      rsyserr(FERROR, errno, "mkdir failed: %s", dirname);
 +                      free(hashname);
 +                      free(dirname);
 +                      free(hashname);
 +                      free(dirname);
-+                      return robust_rename(fnametmp,fname,0644);
++                      return robust_rename(fnametmp, fname, NULL, 0644);
 +              }
 +              free(dirname);
 +
 +              if (do_mkdir(hashname, 0755) == -1 && errno != EEXIST) {
 +              }
 +              free(dirname);
 +
 +              if (do_mkdir(hashname, 0755) == -1 && errno != EEXIST) {
-+                      rsyserr(FERROR, errno, "mkdir %s", hashname);
++                      rsyserr(FERROR, errno, "mkdir failed: %s", hashname);
 +                      free(hashname);
 +                      free(hashname);
-+                      return robust_rename(fnametmp,fname,0644);
++                      return robust_rename(fnametmp, fname, NULL, 0644);
 +              }
 +
 +              first = 1;
 +              }
 +
 +              first = 1;
-+              asprintf(&linkname,"%s/0",hashname);
++              if (asprintf(&linkname,"%s/0",hashname) < 0)
++                      out_of_memory("link_by_hash");
 +              rprintf(FINFO, "(1) linkname = %s\n", linkname);
 +              rprintf(FINFO, "(1) linkname = %s\n", linkname);
-+                      
 +      } else {
 +              struct hashfile_struct *hashfiles, *hashfile;
 +      } else {
 +              struct hashfile_struct *hashfiles, *hashfile;
-+              int fd;
 +
 +              if (do_stat(fnametmp,&st) == -1) {
 +
 +              if (do_stat(fnametmp,&st) == -1) {
-+                      rsyserr(FERROR, errno, "%s", fname);
++                      rsyserr(FERROR, errno, "stat failed: %s", fname);
 +                      return -1;
 +              }
 +              hashfiles = find_hashfiles(hashname, st.st_size, &last_fnbr);
 +
 +              if (hashfiles == NULL) {
 +                      first = 1;
 +                      return -1;
 +              }
 +              hashfiles = find_hashfiles(hashname, st.st_size, &last_fnbr);
 +
 +              if (hashfiles == NULL) {
 +                      first = 1;
-+                      asprintf(&linkname,"%s/0",hashname);
++                      if (asprintf(&linkname,"%s/0",hashname) < 0)
++                              out_of_memory("link_by_hash");
 +                      rprintf(FINFO, "(2) linkname = %s\n", linkname);
 +              } else {
 +                      rprintf(FINFO, "(2) linkname = %s\n", linkname);
 +              } else {
-+                      
++                      int fd;
 +                      /* Search for one identical to us. */
 +                      if ((fd = open(fnametmp,O_RDONLY|O_BINARY)) == -1) {
 +                      /* Search for one identical to us. */
 +                      if ((fd = open(fnametmp,O_RDONLY|O_BINARY)) == -1) {
-+                              rsyserr(FERROR, errno, "%s", fnametmp);
++                              rsyserr(FERROR, errno, "open failed: %s", fnametmp);
 +                              kill_hashfiles(hashfiles);
 +                              return -1;
 +                      }
 +                      hashfile = compare_hashfiles(fd, hashfiles);
 +                      hashfiles = NULL;
 +                              kill_hashfiles(hashfiles);
 +                              return -1;
 +                      }
 +                      hashfile = compare_hashfiles(fd, hashfiles);
 +                      hashfiles = NULL;
++                      close(fd);
 +
 +                      if (hashfile) {
 +                              first = 0;
 +
 +                      if (hashfile) {
 +                              first = 0;
@@ -315,8 +338,8 @@ the file's name.
 +                              kill_hashfile(hashfile);
 +                      } else {
 +                              first = 1;
 +                              kill_hashfile(hashfile);
 +                      } else {
 +                              first = 1;
-+                              asprintf(&linkname, "%s/%ld", hashname,
-+                                       last_fnbr + 1);
++                              if (asprintf(&linkname, "%s/%ld", hashname, last_fnbr + 1) < 0)
++                                      out_of_memory("link_by_hash");
 +                              rprintf(FINFO, "(4) linkname = %s\n", linkname);
 +                      }
 +              }
 +                              rprintf(FINFO, "(4) linkname = %s\n", linkname);
 +                      }
 +              }
@@ -325,20 +348,20 @@ the file's name.
 +      if (!first) {
 +              rprintf(FINFO, "link-by-hash (existing): \"%s\" -> %s\n",
 +                              linkname, full_fname(fname));
 +      if (!first) {
 +              rprintf(FINFO, "link-by-hash (existing): \"%s\" -> %s\n",
 +                              linkname, full_fname(fname));
++              robust_unlink(fname);
 +              rc = do_link(linkname, fname);
 +              if (rc == -1) {
 +                      if (errno == EMLINK) {
 +                              first = 1;
 +                              free(linkname);
 +              rc = do_link(linkname, fname);
 +              if (rc == -1) {
 +                      if (errno == EMLINK) {
 +                              first = 1;
 +                              free(linkname);
-+                              asprintf(&linkname,"%s/%ld",hashname,
-+                                       last_fnbr + 1);
++                              if (asprintf(&linkname,"%s/%ld",hashname, last_fnbr + 1) < 0)
++                                      out_of_memory("link_by_hash");
 +                              rprintf(FINFO, "(5) linkname = %s\n", linkname);
 +                              rprintf(FINFO,"link-by-hash: max link count exceeded, starting new file \"%s\".\n", linkname);
 +                      } else {
 +                              rsyserr(FERROR, errno, "link \"%s\" -> \"%s\"",
 +                                      linkname, full_fname(fname));
 +                              rprintf(FINFO, "(5) linkname = %s\n", linkname);
 +                              rprintf(FINFO,"link-by-hash: max link count exceeded, starting new file \"%s\".\n", linkname);
 +                      } else {
 +                              rsyserr(FERROR, errno, "link \"%s\" -> \"%s\"",
 +                                      linkname, full_fname(fname));
-+                              robust_unlink(fname);
-+                              rc = robust_rename(fnametmp,fname,0644);
++                              rc = robust_rename(fnametmp, fname, NULL, 0644);
 +                      }
 +              } else {
 +                      do_unlink(fnametmp);
 +                      }
 +              } else {
 +                      do_unlink(fnametmp);
@@ -349,7 +372,7 @@ the file's name.
 +              rprintf(FINFO, "link-by-hash (new): %s -> \"%s\"\n",
 +                              full_fname(fname),linkname);
 +
 +              rprintf(FINFO, "link-by-hash (new): %s -> \"%s\"\n",
 +                              full_fname(fname),linkname);
 +
-+              rc = robust_rename(fnametmp,fname,0644);
++              rc = robust_rename(fnametmp, fname, NULL, 0644);
 +              if (rc != 0) {
 +                      rsyserr(FERROR, errno, "rename \"%s\" -> \"%s\"",
 +                              full_fname(fnametmp), full_fname(fname));
 +              if (rc != 0) {
 +                      rsyserr(FERROR, errno, "rename \"%s\" -> \"%s\"",
 +                              full_fname(fnametmp), full_fname(fname));
@@ -365,51 +388,53 @@ the file's name.
 +      free(hashname);
 +      return rc;
 +}
 +      free(hashname);
 +      return rc;
 +}
-+
 +#endif
 +#endif
---- orig/options.c     2004-07-15 16:51:50
-+++ options.c  2004-07-03 20:20:15
-@@ -124,6 +124,7 @@ char *log_format = NULL;
- char *password_file = NULL;
- char *rsync_path = RSYNC_PATH;
- char *backup_dir = NULL;
+diff --git a/options.c b/options.c
+--- a/options.c
++++ b/options.c
+@@ -158,6 +158,7 @@ char *backup_suffix = NULL;
+ char *tmpdir = NULL;
+ char *partial_dir = NULL;
+ char *basis_dir[MAX_BASIS_DIRS+1];
 +char *link_by_hash_dir = NULL;
 +char *link_by_hash_dir = NULL;
- char backup_dir_buf[MAXPATHLEN];
- int rsync_port = RSYNC_PORT;
- int link_dest = 0;
-@@ -269,6 +270,7 @@ void usage(enum logcode F)
-   rprintf(F," -T  --temp-dir=DIR          create temporary files in directory DIR\n");
+ char *config_file = NULL;
+ char *shell_cmd = NULL;
+ char *logfile_name = NULL;
+@@ -745,6 +746,7 @@ void usage(enum logcode F)
    rprintf(F,"     --compare-dest=DIR      also compare destination files relative to DIR\n");
    rprintf(F,"     --compare-dest=DIR      also compare destination files relative to DIR\n");
-   rprintf(F,"     --link-dest=DIR         create hardlinks to DIR for unchanged files\n");
-+  rprintf(F,"     --link-by-hash=DIR      create hardlinks by hash to DIR for regular files\n");
-   rprintf(F," -P                          equivalent to --partial --progress\n");
-   rprintf(F," -z, --compress              compress file data\n");
-   rprintf(F," -C, --cvs-exclude           auto ignore files in the same way CVS does\n");
-@@ -309,7 +311,7 @@ void usage(enum logcode F)
- enum {OPT_VERSION = 1000, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
-       OPT_DELETE_AFTER, OPT_DELETE_EXCLUDED, OPT_LINK_DEST,
-       OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW,
--      OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_TIMEOUT,
-+      OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_TIMEOUT, OPT_LINK_BY_HASH,
-       OPT_REFUSED_BASE = 9000};
+   rprintf(F,"     --copy-dest=DIR         ... and include copies of unchanged files\n");
+   rprintf(F,"     --link-dest=DIR         hardlink to files in DIR when unchanged\n");
++  rprintf(F,"     --link-by-hash=DIR      create hardlinks by hash into DIR\n");
+   rprintf(F," -z, --compress              compress file data during the transfer\n");
+   rprintf(F,"     --compress-level=NUM    explicitly set compression level\n");
+   rprintf(F,"     --skip-compress=LIST    skip compressing files with a suffix in LIST\n");
+@@ -798,7 +800,7 @@ enum {OPT_VERSION = 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_USERMAP, OPT_GROUPMAP, OPT_CHOWN,
++      OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_LINK_BY_HASH,
+       OPT_SERVER, OPT_REFUSED_BASE = 9000};
  
  static struct poptOption long_options[] = {
  
  static struct poptOption long_options[] = {
-@@ -367,6 +369,7 @@ static struct poptOption long_options[] 
-   {"temp-dir",        'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
-   {"compare-dest",     0,  POPT_ARG_STRING, &compare_dest, 0, 0, 0 },
-   {"link-dest",        0,  POPT_ARG_STRING, &compare_dest,  OPT_LINK_DEST, 0, 0 },
-+  {"link-by-hash",     0,  POPT_ARG_STRING, 0,              OPT_LINK_BY_HASH, 0, 0},
-   /* TODO: Should this take an optional int giving the compression level? */
-   {"compress",        'z', POPT_ARG_NONE,   &do_compression, 0, 0, 0 },
-   {"daemon",           0,  POPT_ARG_NONE,   &daemon_opt, 0, 0, 0 },
-@@ -593,6 +596,19 @@ int parse_arguments(int *argc, const cha
+@@ -937,6 +939,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 },
++  {"link-by-hash",     0,  POPT_ARG_STRING, 0, OPT_LINK_BY_HASH, 0, 0},
+   {"fuzzy",           'y', POPT_ARG_VAL,    &fuzzy_basis, 1, 0, 0 },
+   {"no-fuzzy",         0,  POPT_ARG_VAL,    &fuzzy_basis, 0, 0, 0 },
+   {"no-y",             0,  POPT_ARG_VAL,    &fuzzy_basis, 0, 0, 0 },
+@@ -1743,6 +1746,21 @@ int parse_arguments(int *argc_p, const char ***argv_p)
                        return 0;
  #endif
  
 +                case OPT_LINK_BY_HASH:
                        return 0;
  #endif
  
 +                case OPT_LINK_BY_HASH:
-+#if HAVE_LINK
-+                      link_by_hash_dir = (char *)poptGetOptArg(pc);
-+                      checksum_seed = FIXED_CHECKSUM_SEED;
++#ifdef HAVE_LINK
++                      arg = poptGetOptArg(pc);
++                      if (sanitize_paths)
++                              arg = sanitize_path(NULL, arg, NULL, 0, SP_DEFAULT);
++                      link_by_hash_dir = (char *)arg;
 +                      break;
 +#else
 +                      snprintf(err_buf, sizeof err_buf,
 +                      break;
 +#else
 +                      snprintf(err_buf, sizeof err_buf,
@@ -421,146 +446,127 @@ the file's name.
 +
                default:
                        /* A large opt value means that set_refuse_options()
 +
                default:
                        /* A large opt value means that set_refuse_options()
-                        * turned this option off (opt-BASE is its index). */
-@@ -969,6 +985,11 @@ void server_options(char **args,int *arg
-               args[ac++] = compare_dest;
-       }
+                        * turned this option off. */
+@@ -2601,6 +2619,11 @@ void server_options(char **args, int *argc_p)
+       } else if (inplace)
+               args[ac++] = "--inplace";
  
 +      if (link_by_hash_dir && am_sender) {
 +              args[ac++] = "--link-by-hash";
 +              args[ac++] = link_by_hash_dir;
 +      }
 +
  
 +      if (link_by_hash_dir && am_sender) {
 +              args[ac++] = "--link-by-hash";
 +              args[ac++] = link_by_hash_dir;
 +      }
 +
-       if (files_from && (!am_sender || remote_filesfrom_file)) {
-               if (remote_filesfrom_file) {
+       if (files_from && (!am_sender || filesfrom_host)) {
+               if (filesfrom_host) {
                        args[ac++] = "--files-from";
                        args[ac++] = "--files-from";
---- orig/receiver.c    2004-07-14 17:12:06
-+++ receiver.c 2004-07-03 20:20:15
-@@ -48,6 +48,7 @@ extern int ignore_errors;
- extern int orig_umask;
- extern int keep_partial;
- extern int checksum_seed;
-+extern char *link_by_hash_dir;
- static void delete_one(char *fn, int is_dir)
- {
-@@ -196,10 +197,11 @@ static int get_tmpname(char *fnametmp, c
+diff --git a/receiver.c b/receiver.c
+--- a/receiver.c
++++ b/receiver.c
+@@ -164,11 +164,13 @@ int open_tmpfile(char *fnametmp, const char *fname, struct file_struct *file)
+ }
  
  
- static int receive_data(int f_in,struct map_struct *mapbuf,int fd,char *fname,
--                      OFF_T total_size)
-+                      OFF_T total_size,char *md4)
+ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
+-                      const char *fname, int fd, OFF_T total_size)
++                      const char *fname, int fd, OFF_T total_size,
++                      const char *md4)
  {
  {
-       int i;
+       static char file_sum1[MAX_DIGEST_LEN];
+       struct map_struct *mapbuf;
        struct sum_struct sum;
        struct sum_struct sum;
-+      struct mdfour mdfour_data;
-       unsigned int len;
++      md_context mdfour_data;
+       int32 len;
        OFF_T offset = 0;
        OFF_T offset2;
        OFF_T offset = 0;
        OFF_T offset2;
-@@ -210,6 +212,9 @@ static int receive_data(int f_in,struct 
-       read_sum_head(f_in, &sum);
+@@ -188,6 +190,9 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
+       } else
+               mapbuf = NULL;
  
 +      if (md4)
 +              mdfour_begin(&mdfour_data);
 +
        sum_init(checksum_seed);
  
  
 +      if (md4)
 +              mdfour_begin(&mdfour_data);
 +
        sum_init(checksum_seed);
  
-       while ((i = recv_token(f_in, &data)) != 0) {
-@@ -226,6 +231,8 @@ static int receive_data(int f_in,struct 
+       if (append_mode > 0) {
+@@ -232,6 +237,8 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
                        cleanup_got_literal = 1;
  
                        cleanup_got_literal = 1;
  
-                       sum_update(data,i);
+                       sum_update(data, i);
 +                      if (md4)
 +                      if (md4)
-+                              mdfour_update(&mdfour_data,data,i);
++                              mdfour_update(&mdfour_data, (uchar*)data, i);
  
  
-                       if (fd != -1 && write_file(fd,data,i) != i) {
-                               rsyserr(FERROR, errno, "write failed on %s",
-@@ -253,6 +260,8 @@ static int receive_data(int f_in,struct 
+                       if (fd != -1 && write_file(fd,data,i) != i)
+                               goto report_write_error;
+@@ -258,6 +265,8 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
  
                        see_token(map, len);
  
                        see_token(map, len);
-                       sum_update(map,len);
+                       sum_update(map, len);
 +                      if (md4)
 +                      if (md4)
-+                              mdfour_update(&mdfour_data,map,len);
++                              mdfour_update(&mdfour_data, (uchar*)map, len);
                }
  
                }
  
-               if (fd != -1 && write_file(fd, map, len) != (int)len) {
-@@ -275,6 +284,8 @@ static int receive_data(int f_in,struct 
-       }
+               if (updating_basis_or_equiv) {
+@@ -305,6 +314,9 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
+       if (sum_end(file_sum1) != checksum_len)
+               overflow_exit("checksum_len"); /* Impossible... */
  
  
-       sum_end(file_sum1);
 +      if (md4)
 +      if (md4)
-+              mdfour_result(&mdfour_data, (unsigned char*)md4);
++              mdfour_result(&mdfour_data, (uchar*)md4);
++
+       if (mapbuf)
+               unmap_file(mapbuf);
  
  
-       read_buf(f_in,file_sum2,MD4_SUM_LENGTH);
-       if (verbose > 2)
-@@ -374,7 +385,7 @@ int recv_files(int f_in, struct file_lis
-               if (fd1 != -1 && do_fstat(fd1,&st) != 0) {
-                       rsyserr(FERROR, errno, "fstat %s failed",
-                               full_fname(fnamecmp));
--                      receive_data(f_in,NULL,-1,NULL,file->length);
-+                      receive_data(f_in,NULL,-1,NULL,file->length,NULL);
-                       close(fd1);
-                       continue;
-               }
-@@ -387,7 +398,7 @@ int recv_files(int f_in, struct file_lis
-                        */
-                       rprintf(FERROR,"recv_files: %s is a directory\n",
-                               full_fname(fnamecmp));
--                      receive_data(f_in, NULL, -1, NULL, file->length);
-+                      receive_data(f_in,NULL,-1,NULL,file->length,NULL);
-                       close(fd1);
-                       continue;
-               }
-@@ -443,7 +454,7 @@ int recv_files(int f_in, struct file_lis
-               if (fd2 == -1) {
-                       rsyserr(FERROR, errno, "mkstemp %s failed",
-                               full_fname(fnametmp));
--                      receive_data(f_in,mapbuf,-1,NULL,file->length);
-+                      receive_data(f_in,mapbuf,-1,NULL,file->length,NULL);
-                       if (mapbuf)
-                               unmap_file(mapbuf);
-                       if (fd1 != -1)
-@@ -457,7 +468,11 @@ int recv_files(int f_in, struct file_lis
-                       rprintf(FINFO, "%s\n", fname);
+@@ -319,7 +331,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
+ static void discard_receive_data(int f_in, OFF_T length)
+ {
+-      receive_data(f_in, NULL, -1, 0, NULL, -1, length);
++      receive_data(f_in, NULL, -1, 0, NULL, -1, length, NULL);
+ }
+ static void handle_delayed_updates(char *local_name)
+@@ -744,7 +756,7 @@ int recv_files(int f_in, char *local_name)
  
                /* recv file data */
  
                /* recv file data */
--              recv_ok = receive_data(f_in,mapbuf,fd2,fname,file->length);
-+#ifdef HAVE_LINK
-+              if (link_by_hash_dir)
-+                      file->u.sum = (char*)malloc(MD4_SUM_LENGTH);
-+#endif
-+              recv_ok = receive_data(f_in,mapbuf,fd2,fname,file->length,file->u.sum);
+               recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size,
+-                                     fname, fd2, F_LENGTH(file));
++                                     fname, fd2, F_LENGTH(file), F_SUM(file));
  
  
-               log_recv(file, &initial_stats);
+               log_item(log_code, file, &initial_stats, iflags, NULL);
  
  
---- orig/rsync.c       2004-07-02 18:06:32
-+++ rsync.c    2004-07-03 20:20:15
-@@ -34,6 +34,7 @@ extern int force_delete;
- extern int recurse;
+diff --git a/rsync.c b/rsync.c
+--- a/rsync.c
++++ b/rsync.c
+@@ -48,6 +48,7 @@ extern int flist_eof;
+ extern int msgs2stderr;
+ extern int keep_dirlinks;
  extern int make_backups;
  extern int make_backups;
- extern char *backup_dir;
 +extern char *link_by_hash_dir;
 +extern char *link_by_hash_dir;
- /*
-@@ -242,6 +243,12 @@ void finish_transfer(char *fname, char *
+ extern struct file_list *cur_flist, *first_flist, *dir_flist;
+ extern struct chmod_mode_struct *daemon_chmod_modes;
+ #ifdef ICONV_OPTION
+@@ -575,8 +576,15 @@ int finish_transfer(const char *fname, const char *fnametmp,
        /* move tmp file over real file */
        /* move tmp file over real file */
-       if (verbose > 2)
+       if (DEBUG_GTE(RECV, 1))
                rprintf(FINFO, "renaming %s to %s\n", fnametmp, fname);
                rprintf(FINFO, "renaming %s to %s\n", fnametmp, fname);
+-      ret = robust_rename(fnametmp, fname, temp_copy_name,
+-                          file->mode & INITACCESSPERMS);
 +#ifdef HAVE_LINK
 +      if (link_by_hash_dir)
 +              ret = link_by_hash(fnametmp, fname, file);
 +      else
 +#endif
 +#ifdef HAVE_LINK
 +      if (link_by_hash_dir)
 +              ret = link_by_hash(fnametmp, fname, file);
 +      else
 +#endif
-+              ret = robust_rename(fnametmp, fname, file->mode & INITACCESSPERMS);
-       ret = robust_rename(fnametmp, fname, file->mode & INITACCESSPERMS);
++      {
++              ret = robust_rename(fnametmp, fname, temp_copy_name,
++                                  file->mode & INITACCESSPERMS);
++      }
        if (ret < 0) {
        if (ret < 0) {
-               rsyserr(FERROR, errno, "%s %s -> \"%s\"",
---- orig/rsync.h       2004-07-07 08:27:00
-+++ rsync.h    2004-07-03 20:20:15
-@@ -522,6 +522,14 @@ struct stats {
-       int current_file_index;
+               rsyserr(FERROR_XFER, errno, "%s %s -> \"%s\"",
+                       ret == -2 ? "copy" : "rename",
+diff --git a/rsync.h b/rsync.h
+--- a/rsync.h
++++ b/rsync.h
+@@ -854,6 +854,14 @@ struct stats {
+       int xferred_files;
  };
  
 +struct hashfile_struct {
  };
  
 +struct hashfile_struct {
@@ -571,6 +577,17 @@ the file's name.
 +      uint32 nlink;
 +};
 +
 +      uint32 nlink;
 +};
 +
+ struct chmod_mode_struct;
  
  
- /* we need this function because of the silly way in which duplicate
-    entries are handled in the file lists - we can't change this
+ struct flist_ndx_item {
+diff --git a/rsync.yo b/rsync.yo
+--- a/rsync.yo
++++ b/rsync.yo
+@@ -400,6 +400,7 @@ to the detailed description below for a complete description.  verb(
+      --compare-dest=DIR      also compare received files relative to DIR
+      --copy-dest=DIR         ... and include copies of unchanged files
+      --link-dest=DIR         hardlink to files in DIR when unchanged
++     --link-by-hash=DIR      create hardlinks by hash into DIR
+  -z, --compress              compress file data during the transfer
+      --compress-level=NUM    explicitly set compression level
+      --skip-compress=LIST    skip compressing files with suffix in LIST