- hard links
authorAndrew Tridgell <tridge@samba.org>
Sun, 30 Jun 1996 03:57:22 +0000 (03:57 +0000)
committerAndrew Tridgell <tridge@samba.org>
Sun, 30 Jun 1996 03:57:22 +0000 (03:57 +0000)
- better sparse handling
- FERROR and FINFO

13 files changed:
Makefile.in
README
compat.c
configure.in
exclude.c
flist.c
hlink.c [new file with mode: 0644]
io.c
main.c
match.c
rsync.c
rsync.h
util.c

index 36e4180..b0a1f8f 100644 (file)
@@ -21,7 +21,7 @@ SHELL=/bin/sh
 
 LIBOBJ=lib/getopt.o lib/fnmatch.o 
 OBJS1=rsync.o exclude.o util.o md4.o main.o checksum.o match.o 
-OBJS=$(OBJS1) flist.o io.o compat.o $(LIBOBJ)
+OBJS=$(OBJS1) flist.o io.o compat.o hlink.o $(LIBOBJ)
 
 .c.o:
        $(CC) $(CFLAGS) -c $*.c -o $*.o
diff --git a/README b/README
index 1440c34..b1651ce 100644 (file)
--- a/README
+++ b/README
@@ -29,11 +29,13 @@ Here is a brief description of available options:
 -b, --backup             make backups (default ~ extension)
 -u, --update             update only (don't overwrite newer files)
 -l, --links              preserve soft links
+-H, --hard-links         preserve hard links
 -p, --perms              preserve permissions
 -o, --owner              preserve owner (root only)
 -g, --group              preserve group
 -D, --devices            preserve devices (root only)
 -t, --times              preserve times
+-S, --sparse             handle sparse files efficiently
 -n, --dry-run            show what would have been transferred
 -x, --one-file-system    don't cross filesystem boundaries
 -B, --block-size SIZE    checksum blocking size
@@ -45,10 +47,10 @@ Here is a brief description of available options:
     --exclude FILE       exclude file FILE
     --exclude-from FILE  exclude files listed in FILE
     --suffix SUFFIX      override backup suffix
+    --csum-length LENGTH set the checksum length
     --version            print version number
 
 
-
 SETUP
 -----
 
index 64808a0..04913c2 100644 (file)
--- a/compat.c
+++ b/compat.c
@@ -43,7 +43,7 @@ void send_file_entry_v10(struct file_struct *file,int f)
 {
   unsigned char flags;
   static mode_t last_mode=0;
-  static dev_t last_dev=0;
+  static dev_t last_rdev=0;
   static uid_t last_uid=0;
   static gid_t last_gid=0;
   static char lastdir[MAXPATHLEN]="";
@@ -59,7 +59,7 @@ void send_file_entry_v10(struct file_struct *file,int f)
   flags = FILE_VALID;
 
   if (file->mode == last_mode) flags |= SAME_MODE;
-  if (file->dev == last_dev) flags |= SAME_DEV;
+  if (file->rdev == last_rdev) flags |= SAME_RDEV;
   if (file->uid == last_uid) flags |= SAME_UID;
   if (file->gid == last_gid) flags |= SAME_GID;
     
@@ -84,8 +84,8 @@ void send_file_entry_v10(struct file_struct *file,int f)
     write_int(f,(int)file->uid);
   if (preserve_gid && !(flags & SAME_GID))
     write_int(f,(int)file->gid);
-  if (preserve_devices && IS_DEVICE(file->mode) && !(flags & SAME_DEV))
-    write_int(f,(int)file->dev);
+  if (preserve_devices && IS_DEVICE(file->mode) && !(flags & SAME_RDEV))
+    write_int(f,(int)file->rdev);
 
 #if SUPPORT_LINKS
   if (preserve_links && S_ISLNK(file->mode)) {
@@ -99,7 +99,7 @@ void send_file_entry_v10(struct file_struct *file,int f)
   }       
 
   last_mode = file->mode;
-  last_dev = file->dev;
+  last_rdev = file->rdev;
   last_uid = file->uid;
   last_gid = file->gid;
   p = strrchr(file->name,'/');
@@ -118,7 +118,7 @@ void receive_file_entry_v10(struct file_struct *file,
                            unsigned char flags,int f)
 {
   static mode_t last_mode=0;
-  static dev_t last_dev=0;
+  static dev_t last_rdev=0;
   static uid_t last_uid=0;
   static gid_t last_gid=0;
   static char lastdir[MAXPATHLEN]="";
@@ -148,7 +148,7 @@ void receive_file_entry_v10(struct file_struct *file,
   if (preserve_gid)
     file->gid = (flags & SAME_GID) ? last_gid : (gid_t)read_int(f);
   if (preserve_devices && IS_DEVICE(file->mode))
-    file->dev = (flags & SAME_DEV) ? last_dev : (dev_t)read_int(f);
+    file->rdev = (flags & SAME_RDEV) ? last_rdev : (dev_t)read_int(f);
 
 #if SUPPORT_LINKS
   if (preserve_links && S_ISLNK(file->mode)) {
@@ -164,7 +164,7 @@ void receive_file_entry_v10(struct file_struct *file,
     read_buf(f,file->sum,csum_length);
   
   last_mode = file->mode;
-  last_dev = file->dev;
+  last_rdev = file->rdev;
   last_uid = file->uid;
   last_gid = file->gid;
   p = strrchr(file->name,'/');
index 0d34b4a..46ab168 100644 (file)
@@ -28,6 +28,7 @@ AC_TYPE_OFF_T
 AC_TYPE_SIZE_T
 AC_TYPE_PID_T
 AC_STRUCT_ST_RDEV
+AC_CHECK_TYPE(ino_t,unsigned)
 
 echo $ac_n "checking for errno in errno.h... $ac_c"
 AC_TRY_COMPILE([#include <errno.h>],[int i = errno],
@@ -38,7 +39,7 @@ AC_FUNC_MEMCMP
 AC_FUNC_MMAP
 AC_FUNC_UTIME_NULL
 AC_CHECK_FUNCS(waitpid strtok pipe getcwd mkdir strdup strerror chown chmod mknod)
-AC_CHECK_FUNCS(fchmod fstat strchr bcopy bzero readlink utime utimes)
+AC_CHECK_FUNCS(fchmod fstat strchr bcopy bzero readlink link utime utimes)
 AC_CHECK_FUNCS(memmove getopt_long)
 
 echo $ac_n "checking for working fnmatch... $ac_c"
index 9a83741..e82bafa 100644 (file)
--- a/exclude.c
+++ b/exclude.c
@@ -86,7 +86,7 @@ void add_exclude_list(char *pattern,char ***list)
 
   if (strcmp(pattern,"!") == 0) {
     if (verbose > 2)
-      fprintf(stderr,"clearing exclude list\n");
+      fprintf(FERROR,"clearing exclude list\n");
     while ((len)--) 
       free((*list)[len]);
     free((*list));
@@ -104,7 +104,7 @@ void add_exclude_list(char *pattern,char ***list)
     out_of_memory("add_exclude");
 
   if (verbose > 2)
-    fprintf(stderr,"add_exclude(%s)\n",pattern);
+    fprintf(FERROR,"add_exclude(%s)\n",pattern);
   
   (*list)[len+1] = NULL;
 }
@@ -121,7 +121,7 @@ char **make_exclude_list(char *fname,char **list1,int fatal)
   char line[MAXPATHLEN];
   if (!f) {
     if (fatal) {
-      fprintf(stderr,"%s : %s\n",fname,strerror(errno));
+      fprintf(FERROR,"%s : %s\n",fname,strerror(errno));
       exit_cleanup(1);
     }
     return list;
diff --git a/flist.c b/flist.c
index fad433a..38569aa 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -33,6 +33,7 @@ extern int cvs_exclude;
 extern int one_file_system;
 extern int make_backups;
 extern int preserve_links;
+extern int preserve_hard_links;
 extern int preserve_perms;
 extern int preserve_devices;
 extern int preserve_uid;
@@ -52,7 +53,7 @@ static int match_file_name(char *fname,struct stat *st)
 {
   if (check_exclude(fname,local_exclude_list)) {
     if (verbose > 2)
-      fprintf(stderr,"excluding file %s\n",fname);
+      fprintf(FERROR,"excluding file %s\n",fname);
     return 0;
   }
   return 1;
@@ -83,7 +84,7 @@ void send_file_entry_v11(struct file_struct *file,int f)
   unsigned char flags;
   static time_t last_time=0;
   static mode_t last_mode=0;
-  static dev_t last_dev=0;
+  static dev_t last_rdev=0;
   static uid_t last_uid=0;
   static gid_t last_gid=0;
   static char lastname[MAXPATHLEN]="";
@@ -99,7 +100,7 @@ void send_file_entry_v11(struct file_struct *file,int f)
   flags = FILE_VALID;
 
   if (file->mode == last_mode) flags |= SAME_MODE;
-  if (file->dev == last_dev) flags |= SAME_DEV;
+  if (file->rdev == last_rdev) flags |= SAME_RDEV;
   if (file->uid == last_uid) flags |= SAME_UID;
   if (file->gid == last_gid) flags |= SAME_GID;
   if (file->modtime == last_time) flags |= SAME_TIME;
@@ -128,8 +129,8 @@ void send_file_entry_v11(struct file_struct *file,int f)
     write_int(f,(int)file->uid);
   if (preserve_gid && !(flags & SAME_GID))
     write_int(f,(int)file->gid);
-  if (preserve_devices && IS_DEVICE(file->mode) && !(flags & SAME_DEV))
-    write_int(f,(int)file->dev);
+  if (preserve_devices && IS_DEVICE(file->mode) && !(flags & SAME_RDEV))
+    write_int(f,(int)file->rdev);
 
 #if SUPPORT_LINKS
   if (preserve_links && S_ISLNK(file->mode)) {
@@ -138,12 +139,19 @@ void send_file_entry_v11(struct file_struct *file,int f)
   }
 #endif
 
+#if SUPPORT_HARD_LINKS
+  if (preserve_hard_links && S_ISREG(file->mode)) {
+    write_int(f,file->dev);
+    write_int(f,file->inode);
+  }
+#endif
+
   if (always_checksum) {
     write_buf(f,file->sum,csum_length);
   }       
 
   last_mode = file->mode;
-  last_dev = file->dev;
+  last_rdev = file->rdev;
   last_uid = file->uid;
   last_gid = file->gid;
   last_time = file->modtime;
@@ -159,7 +167,7 @@ void receive_file_entry_v11(struct file_struct *file,
 {
   static mode_t last_time=0;
   static mode_t last_mode=0;
-  static dev_t last_dev=0;
+  static dev_t last_rdev=0;
   static uid_t last_uid=0;
   static gid_t last_gid=0;
   static char lastname[MAXPATHLEN]="";
@@ -173,6 +181,8 @@ void receive_file_entry_v11(struct file_struct *file,
   else
     l2 = read_byte(f);
 
+  bzero((char *)file,sizeof(*file));
+
   file->name = (char *)malloc(l1+l2+1);
   if (!file->name) out_of_memory("receive_file_entry");
 
@@ -188,7 +198,7 @@ void receive_file_entry_v11(struct file_struct *file,
   if (preserve_gid)
     file->gid = (flags & SAME_GID) ? last_gid : (gid_t)read_int(f);
   if (preserve_devices && IS_DEVICE(file->mode))
-    file->dev = (flags & SAME_DEV) ? last_dev : (dev_t)read_int(f);
+    file->rdev = (flags & SAME_RDEV) ? last_rdev : (dev_t)read_int(f);
 
 #if SUPPORT_LINKS
   if (preserve_links && S_ISLNK(file->mode)) {
@@ -199,12 +209,19 @@ void receive_file_entry_v11(struct file_struct *file,
     file->link[l] = 0;
   }
 #endif
+
+#if SUPPORT_HARD_LINKS
+  if (preserve_hard_links && S_ISREG(file->mode)) {
+    file->dev = read_int(f);
+    file->inode = read_int(f);
+  }
+#endif
   
   if (always_checksum)
     read_buf(f,file->sum,csum_length);
   
   last_mode = file->mode;
-  last_dev = file->dev;
+  last_rdev = file->rdev;
   last_uid = file->uid;
   last_gid = file->gid;
   last_time = file->modtime;
@@ -224,13 +241,13 @@ static struct file_struct *make_file(int recurse,char *fname)
   bzero(sum,SUM_LENGTH);
 
   if (lstat(fname,&st) != 0) {
-    fprintf(stderr,"%s: %s\n",
+    fprintf(FERROR,"%s: %s\n",
            fname,strerror(errno));
     return NULL;
   }
 
   if (S_ISDIR(st.st_mode) && !recurse) {
-    fprintf(stderr,"skipping directory %s\n",fname);
+    fprintf(FERROR,"skipping directory %s\n",fname);
     return NULL;
   }
 
@@ -241,7 +258,9 @@ static struct file_struct *make_file(int recurse,char *fname)
     return NULL;
 
   if (verbose > 2)
-    fprintf(stderr,"make_file(%s)\n",fname);
+    fprintf(FERROR,"make_file(%s)\n",fname);
+
+  bzero((char *)&file,sizeof(file));
 
   file.name = strdup(fname);
   file.modtime = st.st_mtime;
@@ -249,8 +268,10 @@ static struct file_struct *make_file(int recurse,char *fname)
   file.mode = st.st_mode;
   file.uid = st.st_uid;
   file.gid = st.st_gid;
+  file.dev = st.st_dev;
+  file.inode = st.st_ino;
 #ifdef HAVE_ST_RDEV
-  file.dev = st.st_rdev;
+  file.rdev = st.st_rdev;
 #endif
 
 #if SUPPORT_LINKS
@@ -258,7 +279,7 @@ static struct file_struct *make_file(int recurse,char *fname)
     int l;
     char lnk[MAXPATHLEN];
     if ((l=readlink(fname,lnk,MAXPATHLEN-1)) == -1) {
-      fprintf(stderr,"readlink %s : %s\n",fname,strerror(errno));
+      fprintf(FERROR,"readlink %s : %s\n",fname,strerror(errno));
       return NULL;
     }
     lnk[l] = 0;
@@ -326,7 +347,7 @@ static void send_directory(int f,struct file_list *flist,char *dir)
 
   d = opendir(dir);
   if (!d) {
-    fprintf(stderr,"%s: %s\n",
+    fprintf(FERROR,"%s: %s\n",
            dir,strerror(errno));
     return;
   }
@@ -364,8 +385,8 @@ struct file_list *send_file_list(int f,int recurse,int argc,char *argv[])
   struct file_list *flist;
 
   if (verbose && recurse) {
-    fprintf(am_server?stderr:stdout,"building file list ... ");
-    fflush(am_server?stderr:stdout);
+    fprintf(FINFO,"building file list ... ");
+    fflush(FINFO);
   }
 
   flist = (struct file_list *)malloc(sizeof(flist[0]));
@@ -389,12 +410,12 @@ struct file_list *send_file_list(int f,int recurse,int argc,char *argv[])
     }
 
     if (lstat(fname,&st) != 0) {
-      fprintf(stderr,"%s : %s\n",fname,strerror(errno));
+      fprintf(FERROR,"%s : %s\n",fname,strerror(errno));
       continue;
     }
 
     if (S_ISDIR(st.st_mode) && !recurse) {
-      fprintf(stderr,"skipping directory %s\n",fname);
+      fprintf(FERROR,"skipping directory %s\n",fname);
       continue;
     }
 
@@ -410,11 +431,11 @@ struct file_list *send_file_list(int f,int recurse,int argc,char *argv[])
 
     if (dir && *dir) {
       if (getcwd(dbuf,MAXPATHLEN-1) == NULL) {
-       fprintf(stderr,"getwd : %s\n",strerror(errno));
+       fprintf(FERROR,"getwd : %s\n",strerror(errno));
        exit_cleanup(1);
       }
       if (chdir(dir) != 0) {
-       fprintf(stderr,"chdir %s : %s\n",dir,strerror(errno));
+       fprintf(FERROR,"chdir %s : %s\n",dir,strerror(errno));
        continue;
       }
       flist_dir = dir;
@@ -423,7 +444,7 @@ struct file_list *send_file_list(int f,int recurse,int argc,char *argv[])
       send_file_name(f,flist,recurse,fname);
       flist_dir = NULL;
       if (chdir(dbuf) != 0) {
-       fprintf(stderr,"chdir %s : %s\n",dbuf,strerror(errno));
+       fprintf(FERROR,"chdir %s : %s\n",dbuf,strerror(errno));
        exit_cleanup(1);
       }
       continue;
@@ -439,10 +460,10 @@ struct file_list *send_file_list(int f,int recurse,int argc,char *argv[])
     write_flush(f);
   }
 
-  clean_flist(flist);
-
   if (verbose && recurse)
-    fprintf(am_server?stderr:stdout,"done\n");
+    fprintf(FINFO,"done\n");
+
+  clean_flist(flist);
 
   return flist;
 }
@@ -454,7 +475,7 @@ struct file_list *recv_file_list(int f)
   unsigned char flags;
 
   if (verbose > 2)
-    fprintf(stderr,"recv_file_list starting\n");
+    fprintf(FERROR,"recv_file_list starting\n");
 
   flist = (struct file_list *)malloc(sizeof(flist[0]));
   if (!flist)
@@ -488,12 +509,12 @@ struct file_list *recv_file_list(int f)
     flist->count++;
 
     if (verbose > 2)
-      fprintf(stderr,"recv_file_name(%s)\n",flist->files[i].name);
+      fprintf(FERROR,"recv_file_name(%s)\n",flist->files[i].name);
   }
 
 
   if (verbose > 2)
-    fprintf(stderr,"received %d names\n",flist->count);
+    fprintf(FERROR,"received %d names\n",flist->count);
 
   clean_flist(flist);
 
@@ -505,7 +526,7 @@ oom:
 }
 
 
-static int flist_compare(struct file_struct *f1,struct file_struct *f2)
+int file_compare(struct file_struct *f1,struct file_struct *f2)
 {
   if (!f1->name && !f2->name) return 0;
   if (!f1->name) return -1;
@@ -520,14 +541,14 @@ int flist_find(struct file_list *flist,struct file_struct *f)
 
   while (low != high) {
     int mid = (low+high)/2;
-    int ret = flist_compare(&flist->files[mid],f);
+    int ret = file_compare(&flist->files[mid],f);
     if (ret == 0) return mid;
     if (ret > 0) 
       high=mid;
     else
       low=mid+1;
   }
-  if (flist_compare(&flist->files[low],f) == 0)
+  if (file_compare(&flist->files[low],f) == 0)
     return low;
   return -1;
 }
@@ -594,17 +615,17 @@ void clean_flist(struct file_list *flist)
       
   qsort(flist->files,flist->count,
        sizeof(flist->files[0]),
-       (int (*)())flist_compare);
+       (int (*)())file_compare);
 
   for (i=1;i<flist->count;i++) {
     if (flist->files[i].name &&
        strcmp(flist->files[i].name,flist->files[i-1].name) == 0) {
       if (verbose > 1 && !am_server)
-       fprintf(stderr,"removing duplicate name %s from file list\n",
-               flist->files[i].name);
+       fprintf(FERROR,"removing duplicate name %s from file list %d\n",
+               flist->files[i-1].name,i-1);
       free(flist->files[i-1].name);
-      flist->files[i-1].name = NULL;
-    }
+      bzero((char *)&flist->files[i-1],sizeof(flist->files[i-1]));
+    } 
   }
 }
 
diff --git a/hlink.c b/hlink.c
new file mode 100644 (file)
index 0000000..3213b2e
--- /dev/null
+++ b/hlink.c
@@ -0,0 +1,138 @@
+/* 
+   Copyright (C) Andrew Tridgell 1996
+   Copyright (C) Paul Mackerras 1996
+   
+   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 2 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, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "rsync.h"
+
+extern int am_server;
+extern int dry_run;
+
+#if SUPPORT_HARD_LINKS
+static int hlink_compare(struct file_struct *f1,struct file_struct *f2)
+{
+  if (!S_ISREG(f1->mode) && !S_ISREG(f2->mode)) return 0;
+  if (!S_ISREG(f1->mode)) return -1;
+  if (!S_ISREG(f2->mode)) return 1;
+
+  if (f1->dev != f2->dev) 
+    return (f1->dev - f2->dev);
+
+  if (f1->inode != f2->inode) 
+    return (f1->inode - f2->inode);
+
+  return file_compare(f1,f2);
+}
+
+
+static struct file_struct *hlink_list = NULL;
+static int hlink_count=0;
+#endif
+
+void init_hard_links(struct file_list *flist)
+{
+#if SUPPORT_HARD_LINKS
+  if (flist->count < 2) return;
+
+  if (hlink_list) free(hlink_list);
+    
+  if (!(hlink_list = 
+       (struct file_struct *)malloc(sizeof(hlink_list[0])*flist->count)))
+    out_of_memory("init_hard_links");
+
+  bcopy((char *)flist->files,hlink_list,sizeof(hlink_list[0])*flist->count);
+
+  qsort(hlink_list,flist->count,
+       sizeof(hlink_list[0]),
+       (int (*)())hlink_compare);
+
+  hlink_count=flist->count;
+#endif
+}
+
+/* check if a file should be skipped because it is the same as an
+   earlier hard link */
+int check_hard_link(struct file_struct *file)
+{
+#if SUPPORT_HARD_LINKS
+  int low=0,high=hlink_count;
+  int mid=0,ret=0;
+
+  if (!hlink_list || !S_ISREG(file->mode)) return 0;
+
+  while (low != high) {
+    mid = (low+high)/2;
+    ret = hlink_compare(&hlink_list[mid],file);
+    if (ret == 0) break;
+    if (ret > 0) 
+      high=mid;
+    else
+      low=mid+1;
+  }
+
+  if (hlink_compare(&hlink_list[mid],file) != 0) return 0;
+
+  if (mid > 0 &&
+      S_ISREG(hlink_list[mid-1].mode) &&
+      file->dev == hlink_list[mid-1].dev &&
+      file->inode == hlink_list[mid-1].inode)
+    return 1;
+#endif
+
+  return 0;
+}
+
+
+/* create any hard links in the flist */
+void do_hard_links(struct file_list *flist)
+{
+#if SUPPORT_HARD_LINKS
+  int i;
+  
+  if (!hlink_list) return;
+
+  for (i=1;i<hlink_count;i++) {
+    if (S_ISREG(hlink_list[i].mode) &&
+       S_ISREG(hlink_list[i-1].mode) &&
+       hlink_list[i].name && hlink_list[i-1].name &&
+       hlink_list[i].dev == hlink_list[i-1].dev &&
+       hlink_list[i].inode == hlink_list[i-1].inode) {
+      struct stat st1,st2;
+
+      if (stat(hlink_list[i-1].name,&st1) != 0) continue;
+      if (stat(hlink_list[i].name,&st2) != 0) {
+       if (!dry_run && link(hlink_list[i-1].name,hlink_list[i].name) != 0) {
+         fprintf(FINFO,"link %s => %s : %s\n",
+                 hlink_list[i].name,hlink_list[i-1].name,strerror(errno));
+         continue;
+       }
+      } else {
+       if (st2.st_dev == st1.st_dev && st2.st_ino == st1.st_ino) continue;
+       
+       if (!dry_run && (unlink(hlink_list[i].name) != 0 ||
+                        link(hlink_list[i-1].name,hlink_list[i].name) != 0)) {
+         fprintf(FINFO,"link %s => %s : %s\n",
+                 hlink_list[i].name,hlink_list[i-1].name,strerror(errno));
+         continue;
+       }
+      }
+      fprintf(FINFO,"%s => %s\n",
+             hlink_list[i].name,hlink_list[i-1].name);
+    }  
+  }
+#endif
+}
diff --git a/io.c b/io.c
index 9b6bdc8..3c55dbe 100644 (file)
--- a/io.c
+++ b/io.c
@@ -28,6 +28,7 @@ static int total_written = 0;
 static int total_read = 0;
 
 extern int verbose;
+extern int sparse_files;
 
 int write_total(void)
 {
@@ -128,7 +129,7 @@ int read_int(int f)
   char b[4];
   if ((ret=readfd(f,b,4)) != 4) {
     if (verbose > 1) 
-      fprintf(stderr,"Error reading %d bytes : %s\n",
+      fprintf(FERROR,"Error reading %d bytes : %s\n",
              4,ret==-1?strerror(errno):"EOF");
     exit_cleanup(1);
   }
@@ -141,7 +142,7 @@ void read_buf(int f,char *buf,int len)
   int ret;
   if ((ret=readfd(f,buf,len)) != len) {
     if (verbose > 1) 
-      fprintf(stderr,"Error reading %d bytes : %s\n",
+      fprintf(FERROR,"Error reading %d bytes : %s\n",
              len,ret==-1?strerror(errno):"EOF");
     exit_cleanup(1);
   }
@@ -161,43 +162,51 @@ static int last_sparse = 0;
 
 int sparse_end(int f)
 {
-#if SPARSE_FILES
   if (last_sparse) {
     lseek(f,-1,SEEK_CUR);
     return (write(f,&last_byte,1) == 1 ? 0 : -1);
   }
-#endif  
   last_sparse = 0;
   return 0;
 }
 
 int write_sparse(int f,char *buf,int len)
 {
-  int l=0;
+  int l1=0,l2=0;
+  int ret;
 
-#if SPARSE_FILES
-  for (l=0;l<len && buf[l]==0;l++) ;
+  if (!sparse_files) 
+    return write(f,buf,len);
 
-  if (l > 0)
-    lseek(f,l,SEEK_CUR);
+  for (l1=0;l1<len && buf[l1]==0;l1++) ;
+  for (l2=0;l2<(len-l1) && buf[len-(l2+1)]==0;l2++) ;
 
   last_byte = buf[len-1];
-#endif
 
-  if (l == len) {
-    last_sparse = 1;
+  if (l1 == len || l2 > 0)
+    last_sparse=1;
+
+  if (l1 > 0)
+    lseek(f,l1,SEEK_CUR);  
+
+  if (l1 == len) 
     return len;
-  } 
 
-  last_sparse = 0;
+  if ((ret=write(f,buf+l1,len-(l1+l2))) != len-(l1+l2)) {
+    if (ret == -1 || ret == 0) return ret;
+    return (l1+ret);
+  }
+
+  if (l2 > 0)
+    lseek(f,l2,SEEK_CUR);
 
-  return (l + write(f,buf+l,len-l));
+  return len;
 }
 
 int read_write(int fd_in,int fd_out,int size)
 {
   static char *buf=NULL;
-  static int bufsize = CHUNK_SIZE;
+  int bufsize = sparse_files?SPARSE_WRITE_SIZE:WRITE_SIZE;
   int total=0;
   
   if (!buf) {
@@ -257,7 +266,7 @@ void write_int(int f,int x)
   char b[4];
   SIVAL(b,0,x);
   if ((ret=writefd(f,b,4)) != 4) {
-    fprintf(stderr,"write_int failed : %s\n",
+    fprintf(FERROR,"write_int failed : %s\n",
            ret==-1?strerror(errno):"EOF");
     exit_cleanup(1);
   }
@@ -268,7 +277,7 @@ void write_buf(int f,char *buf,int len)
 {
   int ret;
   if ((ret=writefd(f,buf,len)) != len) {
-    fprintf(stderr,"write_buf failed : %s\n",
+    fprintf(FERROR,"write_buf failed : %s\n",
            ret==-1?strerror(errno):"EOF");
     exit_cleanup(1);
   }
diff --git a/main.c b/main.c
index ceed01d..a088720 100644 (file)
--- a/main.c
+++ b/main.c
@@ -31,6 +31,7 @@ static char *rsync_path = RSYNC_NAME;
 
 int make_backups = 0;
 int preserve_links = 0;
+int preserve_hard_links = 0;
 int preserve_perms = 0;
 int preserve_devices = 0;
 int preserve_uid = 0;
@@ -44,6 +45,7 @@ int ignore_times=0;
 int delete_mode=0;
 int one_file_system=0;
 int remote_version=0;
+int sparse_files=0;
 extern int csum_length;
 
 int am_server = 0;
@@ -109,6 +111,8 @@ static void server_options(char **args,int *argc)
     argstr[x++] = 'n';
   if (preserve_links)
     argstr[x++] = 'l';
+  if (preserve_hard_links)
+    argstr[x++] = 'H';
   if (preserve_uid)
     argstr[x++] = 'o';
   if (preserve_gid)
@@ -129,6 +133,8 @@ static void server_options(char **args,int *argc)
     argstr[x++] = 'I';
   if (one_file_system)
     argstr[x++] = 'x';
+  if (sparse_files)
+    argstr[x++] = 'S';
   argstr[x] = 0;
 
   if (x != 1) args[ac++] = argstr;
@@ -199,10 +205,10 @@ int do_cmd(char *cmd,char *machine,char *user,char *path,int *f_in,int *f_out)
   args[argc] = NULL;
 
   if (verbose > 3) {
-    fprintf(stderr,"cmd=");
+    fprintf(FERROR,"cmd=");
     for (i=0;i<argc;i++)
-      fprintf(stderr,"%s ",args[i]);
-    fprintf(stderr,"\n");
+      fprintf(FERROR,"%s ",args[i]);
+    fprintf(FERROR,"\n");
   }
 
   return piped_child(args,f_in,f_out);
@@ -222,13 +228,13 @@ static char *get_local_name(struct file_list *flist,char *name)
   if (stat(name,&st) == 0) {
     if (S_ISDIR(st.st_mode)) {
       if (chdir(name) != 0) {
-       fprintf(stderr,"chdir %s : %s\n",name,strerror(errno));
+       fprintf(FERROR,"chdir %s : %s\n",name,strerror(errno));
        exit_cleanup(1);
       }
       return NULL;
     }
     if (flist->count > 1) {
-      fprintf(stderr,"ERROR: destination must be a directory when copying more than 1 file\n");
+      fprintf(FERROR,"ERROR: destination must be a directory when copying more than 1 file\n");
       exit_cleanup(1);
     }
     return name;
@@ -241,14 +247,14 @@ static char *get_local_name(struct file_list *flist,char *name)
     return NULL;
 
   if (mkdir(name,0777) != 0) {
-    fprintf(stderr,"mkdir %s : %s\n",name,strerror(errno));
+    fprintf(FERROR,"mkdir %s : %s\n",name,strerror(errno));
     exit_cleanup(1);
   } else {
-    fprintf(am_server?stderr:stdout,"created directory %s\n",name);
+    fprintf(FINFO,"created directory %s\n",name);
   }
 
   if (chdir(name) != 0) {
-    fprintf(stderr,"chdir %s : %s\n",name,strerror(errno));
+    fprintf(FERROR,"chdir %s : %s\n",name,strerror(errno));
     exit_cleanup(1);
   }
 
@@ -265,10 +271,10 @@ void do_server_sender(int argc,char *argv[])
   struct file_list *flist;
 
   if (verbose > 2)
-    fprintf(stderr,"server_sender starting pid=%d\n",(int)getpid());
+    fprintf(FERROR,"server_sender starting pid=%d\n",(int)getpid());
   
   if (chdir(dir) != 0) {
-    fprintf(stderr,"chdir %s: %s\n",dir,strerror(errno));
+    fprintf(FERROR,"chdir %s: %s\n",dir,strerror(errno));
     exit_cleanup(1);
   }
   argc--;
@@ -294,23 +300,47 @@ void do_server_sender(int argc,char *argv[])
 }
 
 
+static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name)
+{
+  int pid;
+  int status=0;
+
+  if (preserve_hard_links)
+    init_hard_links(flist);
+
+  if ((pid=fork()) == 0) {
+    recv_files(f_in,flist,local_name);
+    if (preserve_hard_links)
+      do_hard_links(flist);
+    if (verbose > 2)
+      fprintf(FERROR,"receiver read %d\n",read_total());
+    exit_cleanup(0);
+  }
+
+  generate_files(f_out,flist,local_name);
+
+  waitpid(pid, &status, 0);
+
+  return status;
+}
+
 
 void do_server_recv(int argc,char *argv[])
 {
-  int pid,status;
+  int status;
   char *dir = NULL;
   struct file_list *flist;
   char *local_name=NULL;
   
   if (verbose > 2)
-    fprintf(stderr,"server_recv(%d) starting pid=%d\n",argc,(int)getpid());
+    fprintf(FERROR,"server_recv(%d) starting pid=%d\n",argc,(int)getpid());
 
   if (argc > 0) {
     dir = argv[0];
     argc--;
     argv++;
     if (chdir(dir) != 0) {
-      fprintf(stderr,"chdir %s : %s\n",dir,strerror(errno));
+      fprintf(FERROR,"chdir %s : %s\n",dir,strerror(errno));
       exit_cleanup(1);
     }    
   }
@@ -320,7 +350,7 @@ void do_server_recv(int argc,char *argv[])
 
   flist = recv_file_list(STDIN_FILENO);
   if (!flist || flist->count == 0) {
-    fprintf(stderr,"nothing to do\n");
+    fprintf(FERROR,"nothing to do\n");
     exit_cleanup(1);
   }
 
@@ -332,16 +362,7 @@ void do_server_recv(int argc,char *argv[])
     local_name = get_local_name(flist,argv[0]);
   }
 
-  if ((pid=fork()) == 0) {
-    recv_files(STDIN_FILENO,flist,local_name);
-    if (verbose > 2)
-      fprintf(stderr,"receiver read %d\n",read_total());
-    exit_cleanup(0);
-  }
-
-  generate_files(STDOUT_FILENO,flist,local_name);
-
-  waitpid(pid, &status, 0);
+  status = do_recv(STDIN_FILENO,STDOUT_FILENO,flist,local_name);
   exit_cleanup(status);
 }
 
@@ -360,11 +381,13 @@ static void usage(FILE *f)
   fprintf(f,"-b, --backup             make backups (default ~ extension)\n");
   fprintf(f,"-u, --update             update only (don't overwrite newer files)\n");
   fprintf(f,"-l, --links              preserve soft links\n");
+  fprintf(f,"-H, --hard-links         preserve hard links\n");
   fprintf(f,"-p, --perms              preserve permissions\n");
   fprintf(f,"-o, --owner              preserve owner (root only)\n");
   fprintf(f,"-g, --group              preserve group\n");
   fprintf(f,"-D, --devices            preserve devices (root only)\n");
   fprintf(f,"-t, --times              preserve times\n");  
+  fprintf(f,"-S, --sparse             handle sparse files efficiently\n");
   fprintf(f,"-n, --dry-run            show what would have been transferred\n");
   fprintf(f,"-x, --one-file-system    don't cross filesystem boundaries\n");
   fprintf(f,"-B, --block-size SIZE    checksum blocking size\n");  
@@ -387,7 +410,7 @@ static void usage(FILE *f)
 enum {OPT_VERSION,OPT_SUFFIX,OPT_SENDER,OPT_SERVER,OPT_EXCLUDE,
       OPT_EXCLUDE_FROM,OPT_DELETE,OPT_RSYNC_PATH,OPT_CSUM_LENGTH};
 
-static char *short_options = "oblpguDCtcahvrIxne:B:";
+static char *short_options = "oblHpguDCtcahvrIxnSe:B:";
 
 static struct option long_options[] = {
   {"version",     0,     0,    OPT_VERSION},
@@ -402,6 +425,7 @@ static struct option long_options[] = {
   {"ignore-times",0,     0,    'I'},
   {"help",        0,     0,    'h'},
   {"dry-run",     0,     0,    'n'},
+  {"sparse",      0,     0,    'S'},
   {"cvs-exclude", 0,     0,    'C'},
   {"archive",     0,     0,    'a'},
   {"checksum",    0,     0,    'c'},
@@ -412,6 +436,7 @@ static struct option long_options[] = {
   {"devices",     0,     0,    'D'},
   {"perms",       0,     0,    'p'},
   {"links",       0,     0,    'l'},
+  {"hard-links",  0,     0,    'H'},
   {"owner",       0,     0,    'o'},
   {"group",       0,     0,    'g'},
   {"times",       0,     0,    't'},
@@ -422,7 +447,7 @@ static struct option long_options[] = {
 
 int main(int argc,char *argv[])
 {
-    int pid, status, pid2, status2;
+    int pid, status, status2;
     int opt;
     int option_index;
     char *shell_cmd = NULL;
@@ -482,7 +507,7 @@ int main(int argc,char *argv[])
          break;
 
        case 'h':
-         usage(stdout);
+         usage(FINFO);
          exit_cleanup(0);
 
        case 'b':
@@ -493,6 +518,10 @@ int main(int argc,char *argv[])
          dry_run=1;
          break;
 
+       case 'S':
+         sparse_files=1;
+         break;
+
        case 'C':
          cvs_exclude=1;
          break;
@@ -501,11 +530,17 @@ int main(int argc,char *argv[])
          update_only=1;
          break;
 
-#if SUPPORT_LINKS
        case 'l':
+#if SUPPORT_LINKS
          preserve_links=1;
+#endif
          break;
+
+       case 'H':
+#if SUPPORT_HARD_LINKS
+         preserve_hard_links=1;
 #endif
+         break;
 
        case 'p':
          preserve_perms=1;
@@ -515,7 +550,7 @@ int main(int argc,char *argv[])
          if (getuid() == 0) {
            preserve_uid=1;
          } else {
-           fprintf(stderr,"-o only allowed for root\n");
+           fprintf(FERROR,"-o only allowed for root\n");
            exit_cleanup(1);
          }
          break;
@@ -528,7 +563,7 @@ int main(int argc,char *argv[])
          if (getuid() == 0) {
            preserve_devices=1;
          } else {
-           fprintf(stderr,"-D only allowed for root\n");
+           fprintf(FERROR,"-D only allowed for root\n");
            exit_cleanup(1);
          }
          break;
@@ -565,7 +600,7 @@ int main(int argc,char *argv[])
 
        case OPT_SENDER:
          if (!am_server) {
-           usage(stderr);
+           usage(FERROR);
            exit_cleanup(1);
          }
          sender = 1;
@@ -584,7 +619,7 @@ int main(int argc,char *argv[])
          break;
 
        default:
-         fprintf(stderr,"bad option -%c\n",opt);
+         fprintf(FERROR,"bad option -%c\n",opt);
          exit_cleanup(1);
        }
     }
@@ -606,7 +641,7 @@ int main(int argc,char *argv[])
       remote_version = read_int(STDIN_FILENO);
       if (remote_version < MIN_PROTOCOL_VERSION ||
          remote_version > MAX_PROTOCOL_VERSION) {
-       fprintf(stderr,"protocol version mismatch - is your shell clean?\n");
+       fprintf(FERROR,"protocol version mismatch - is your shell clean?\n");
        exit_cleanup(1);
       }
       write_int(STDOUT_FILENO,PROTOCOL_VERSION);
@@ -626,7 +661,7 @@ int main(int argc,char *argv[])
     }
 
     if (argc < 2) {
-      usage(stderr);
+      usage(FERROR);
       exit_cleanup(1);
     }
 
@@ -668,7 +703,7 @@ int main(int argc,char *argv[])
     }
 
     if (verbose > 3) {
-      fprintf(stderr,"cmd=%s machine=%s user=%s path=%s\n",
+      fprintf(FERROR,"cmd=%s machine=%s user=%s path=%s\n",
              shell_cmd?shell_cmd:"",
              shell_machine?shell_machine:"",
              shell_user?shell_user:"",
@@ -676,7 +711,7 @@ int main(int argc,char *argv[])
     }
     
     if (!sender && argc != 1) {
-      usage(stderr);
+      usage(FERROR);
       exit_cleanup(1);
     }
 
@@ -688,7 +723,7 @@ int main(int argc,char *argv[])
       remote_version = read_int(f_in);
       if (remote_version < MIN_PROTOCOL_VERSION ||
          remote_version > MAX_PROTOCOL_VERSION) {
-       fprintf(stderr,"protocol version mismatch - is your shell clean?\n");
+       fprintf(FERROR,"protocol version mismatch - is your shell clean?\n");
        exit_cleanup(1);
       }        
     }
@@ -696,7 +731,7 @@ int main(int argc,char *argv[])
     setup_protocol();
 
     if (verbose > 3) 
-      fprintf(stderr,"parent=%d child=%d sender=%d recurse=%d\n",
+      fprintf(FERROR,"parent=%d child=%d sender=%d recurse=%d\n",
              (int)getpid(),pid,sender,recurse);
 
     if (sender) {
@@ -706,10 +741,10 @@ int main(int argc,char *argv[])
        send_exclude_list(f_out);
       flist = send_file_list(f_out,recurse,argc,argv);
       if (verbose > 3) 
-       fprintf(stderr,"file list sent\n");
+       fprintf(FERROR,"file list sent\n");
       send_files(flist,f_out,f_in);
       if (verbose > 3)
-       fprintf(stderr,"waiting on %d\n",pid);
+       fprintf(FERROR,"waiting on %d\n",pid);
       waitpid(pid, &status, 0);
       report(-1);
       exit_cleanup(status);
@@ -719,22 +754,13 @@ int main(int argc,char *argv[])
 
     flist = recv_file_list(f_in);
     if (!flist || flist->count == 0) {
-      fprintf(stderr,"nothing to do\n");
+      fprintf(FERROR,"nothing to do\n");
       exit_cleanup(0);
     }
 
     local_name = get_local_name(flist,argv[0]);
 
-    if ((pid2=fork()) == 0) {
-      recv_files(f_in,flist,local_name);
-      if (verbose > 1)
-       fprintf(stderr,"receiver read %d\n",read_total());
-      exit_cleanup(0);
-    }
-
-    generate_files(f_out,flist,local_name);
-
-    waitpid(pid2, &status2, 0);
+    status2 = do_recv(f_in,f_out,flist,local_name);
 
     report(f_in);
 
diff --git a/match.c b/match.c
index 2c80727..971fa10 100644 (file)
--- a/match.c
+++ b/match.c
@@ -95,7 +95,7 @@ static void matched(int f,struct sum_struct *s,char *buf,off_t len,
   
   if (verbose > 2)
     if (i != -1)
-      fprintf(stderr,"match at %d last_match=%d j=%d len=%d n=%d\n",
+      fprintf(FERROR,"match at %d last_match=%d j=%d len=%d n=%d\n",
              (int)offset,(int)last_match,i,(int)s->sums[i].len,n);
 
   if (n > 0) {
@@ -125,7 +125,7 @@ static void hash_search(int f,struct sum_struct *s,char *buf,off_t len)
   char *map;
 
   if (verbose > 2)
-    fprintf(stderr,"hash search b=%d len=%d\n",s->n,(int)len);
+    fprintf(FERROR,"hash search b=%d len=%d\n",s->n,(int)len);
 
   k = MIN(len, s->n);
 
@@ -135,21 +135,21 @@ static void hash_search(int f,struct sum_struct *s,char *buf,off_t len)
   s1 = sum & 0xFFFF;
   s2 = sum >> 16;
   if (verbose > 3)
-    fprintf(stderr, "sum=%.8x k=%d\n", sum, k);
+    fprintf(FERROR, "sum=%.8x k=%d\n", sum, k);
 
   offset = 0;
 
   end = len + 1 - s->sums[s->count-1].len;
 
   if (verbose > 3)
-    fprintf(stderr,"hash search s->n=%d len=%d count=%d\n",
+    fprintf(FERROR,"hash search s->n=%d len=%d count=%d\n",
            s->n,(int)len,s->count);
 
   do {
     tag t = gettag2(s1,s2);
     j = tag_table[t];
     if (verbose > 4)
-      fprintf(stderr,"offset=%d sum=%08x\n",
+      fprintf(FERROR,"offset=%d sum=%08x\n",
              offset,sum);
 
     if (j != NULL_TAG) {
@@ -162,7 +162,7 @@ static void hash_search(int f,struct sum_struct *s,char *buf,off_t len)
 
        if (sum == s->sums[i].sum1) {
          if (verbose > 3)
-           fprintf(stderr,"potential match at %d target=%d %d sum=%08x\n",
+           fprintf(FERROR,"potential match at %d target=%d %d sum=%08x\n",
                    offset,j,i,sum);
 
          if (!done_csum2) {
@@ -220,12 +220,12 @@ void match_sums(int f,struct sum_struct *s,char *buf,off_t len)
     build_hash_table(s);
 
     if (verbose > 2) 
-      fprintf(stderr,"built hash table\n");
+      fprintf(FERROR,"built hash table\n");
 
     hash_search(f,s,buf,len);
 
     if (verbose > 2) 
-      fprintf(stderr,"done hash search\n");
+      fprintf(FERROR,"done hash search\n");
   } else {
     matched(f,s,buf,len,len,-1);
   }
@@ -236,7 +236,7 @@ void match_sums(int f,struct sum_struct *s,char *buf,off_t len)
   }
 
   if (verbose > 2)
-    fprintf(stderr, "false_alarms=%d tag_hits=%d matches=%d\n",
+    fprintf(FERROR, "false_alarms=%d tag_hits=%d matches=%d\n",
            false_alarms, tag_hits, matches);
 
   total_tag_hits += tag_hits;
@@ -250,7 +250,7 @@ void match_report(void)
   if (verbose <= 1)
     return;
 
-  fprintf(am_server?stderr:stdout,
+  fprintf(FINFO,
          "total: matches=%d  tag_hits=%d  false_alarms=%d  data=%d\n",
          total_matches,total_tag_hits,
          total_false_alarms,total_data_transfer);
diff --git a/rsync.c b/rsync.c
index bb24980..5ae3695 100644 (file)
--- a/rsync.c
+++ b/rsync.c
@@ -32,6 +32,7 @@ extern int block_size;
 extern int update_only;
 extern int make_backups;
 extern int preserve_links;
+extern int preserve_hard_links;
 extern int preserve_perms;
 extern int preserve_devices;
 extern int preserve_uid;
@@ -105,7 +106,7 @@ static struct sum_struct *generate_sums(char *buf,off_t len,int n)
   }
 
   if (verbose > 3)
-    fprintf(stderr,"count=%d rem=%d n=%d flength=%d\n",
+    fprintf(FERROR,"count=%d rem=%d n=%d flength=%d\n",
            s->count,s->remainder,s->n,(int)s->flength);
 
   s->sums = (struct sum_buf *)malloc(sizeof(s->sums[0])*s->count);
@@ -123,7 +124,7 @@ static struct sum_struct *generate_sums(char *buf,off_t len,int n)
     s->sums[i].i = i;
 
     if (verbose > 3)
-      fprintf(stderr,"chunk[%d] offset=%d len=%d sum1=%08x\n",
+      fprintf(FERROR,"chunk[%d] offset=%d len=%d sum1=%08x\n",
              i,(int)s->sums[i].offset,s->sums[i].len,s->sums[i].sum1);
 
     len -= n1;
@@ -153,7 +154,7 @@ static struct sum_struct *receive_sums(int f)
   s->sums = NULL;
 
   if (verbose > 3)
-    fprintf(stderr,"count=%d n=%d rem=%d\n",
+    fprintf(FERROR,"count=%d n=%d rem=%d\n",
            s->count,s->n,s->remainder);
 
   block_len = s->n;
@@ -179,7 +180,7 @@ static struct sum_struct *receive_sums(int f)
     offset += s->sums[i].len;
 
     if (verbose > 3)
-      fprintf(stderr,"chunk[%d] len=%d offset=%d sum1=%08x\n",
+      fprintf(FERROR,"chunk[%d] len=%d offset=%d sum1=%08x\n",
              i,s->sums[i].len,(int)s->sums[i].offset,s->sums[i].sum1);
   }
 
@@ -199,7 +200,7 @@ static void set_perms(char *fname,struct file_struct *file,struct stat *st,
 
   if (!st) {
     if (stat(fname,&st2) != 0) {
-      fprintf(stderr,"stat %s : %s\n",fname,strerror(errno));
+      fprintf(FERROR,"stat %s : %s\n",fname,strerror(errno));
       return;
     }
     st = &st2;
@@ -209,7 +210,7 @@ static void set_perms(char *fname,struct file_struct *file,struct stat *st,
       st->st_mtime != file->modtime) {
     updated = 1;
     if (set_modtime(fname,file->modtime) != 0) {
-      fprintf(stderr,"failed to set times on %s : %s\n",
+      fprintf(FERROR,"failed to set times on %s : %s\n",
              fname,strerror(errno));
       return;
     }
@@ -220,7 +221,7 @@ static void set_perms(char *fname,struct file_struct *file,struct stat *st,
       st->st_mode != file->mode) {
     updated = 1;
     if (chmod(fname,file->mode) != 0) {
-      fprintf(stderr,"failed to set permissions on %s : %s\n",
+      fprintf(FERROR,"failed to set permissions on %s : %s\n",
              fname,strerror(errno));
       return;
     }
@@ -234,16 +235,16 @@ static void set_perms(char *fname,struct file_struct *file,struct stat *st,
              preserve_uid?file->uid:-1,
              preserve_gid?file->gid:-1) != 0) {
       if (verbose>1 || preserve_uid)
-       fprintf(stderr,"chown %s : %s\n",fname,strerror(errno));
+       fprintf(FERROR,"chown %s : %s\n",fname,strerror(errno));
       return;
     }
   }
     
   if (verbose > 1 && report) {
     if (updated)
-      fprintf(am_server?stderr:stdout,"%s\n",fname);
+      fprintf(FINFO,"%s\n",fname);
     else
-      fprintf(am_server?stderr:stdout,"%s is uptodate\n",fname);
+      fprintf(FINFO,"%s is uptodate\n",fname);
   }
 }
 
@@ -256,66 +257,73 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out)
   struct sum_struct *s;
   char sum[SUM_LENGTH];
   int statret;
+  struct file_struct *file = &flist->files[i];
 
   if (verbose > 2)
-    fprintf(stderr,"recv_generator(%s)\n",fname);
+    fprintf(FERROR,"recv_generator(%s)\n",fname);
 
   statret = lstat(fname,&st);
 
 #if SUPPORT_LINKS
-  if (preserve_links && S_ISLNK(flist->files[i].mode)) {
+  if (preserve_links && S_ISLNK(file->mode)) {
     char lnk[MAXPATHLEN];
     int l;
     if (statret == 0) {
       l = readlink(fname,lnk,MAXPATHLEN-1);
       if (l > 0) {
        lnk[l] = 0;
-       if (strcmp(lnk,flist->files[i].link) == 0) {
-         set_perms(fname,&flist->files[i],&st,1);
+       if (strcmp(lnk,file->link) == 0) {
+         set_perms(fname,file,&st,1);
          return;
        }
       }
     }
     if (!dry_run) unlink(fname);
-    if (!dry_run && symlink(flist->files[i].link,fname) != 0) {
-      fprintf(stderr,"link %s -> %s : %s\n",
-             fname,flist->files[i].link,strerror(errno));
+    if (!dry_run && symlink(file->link,fname) != 0) {
+      fprintf(FERROR,"link %s -> %s : %s\n",
+             fname,file->link,strerror(errno));
     } else {
-      set_perms(fname,&flist->files[i],NULL,0);
+      set_perms(fname,file,NULL,0);
       if (verbose) 
-       fprintf(am_server?stderr:stdout,"%s -> %s\n",
-               fname,flist->files[i].link);
+       fprintf(FINFO,"%s -> %s\n",
+               fname,file->link);
     }
     return;
   }
 #endif
 
 #ifdef HAVE_MKNOD
-  if (preserve_devices && IS_DEVICE(flist->files[i].mode)) {
+  if (preserve_devices && IS_DEVICE(file->mode)) {
     if (statret != 0 || 
-       st.st_mode != flist->files[i].mode ||
-       st.st_rdev != flist->files[i].dev) {    
+       st.st_mode != file->mode ||
+       st.st_rdev != file->rdev) {     
       if (!dry_run) unlink(fname);
       if (verbose > 2)
-       fprintf(stderr,"mknod(%s,0%o,0x%x)\n",
-               fname,(int)flist->files[i].mode,(int)flist->files[i].dev);
+       fprintf(FERROR,"mknod(%s,0%o,0x%x)\n",
+               fname,(int)file->mode,(int)file->rdev);
       if (!dry_run && 
-         mknod(fname,flist->files[i].mode,flist->files[i].dev) != 0) {
-       fprintf(stderr,"mknod %s : %s\n",fname,strerror(errno));
+         mknod(fname,file->mode,file->rdev) != 0) {
+       fprintf(FERROR,"mknod %s : %s\n",fname,strerror(errno));
       } else {
-       set_perms(fname,&flist->files[i],NULL,0);
+       set_perms(fname,file,NULL,0);
        if (verbose)
-         fprintf(am_server?stderr:stdout,"%s\n",fname);
+         fprintf(FINFO,"%s\n",fname);
       }
     } else {
-      set_perms(fname,&flist->files[i],&st,1);
+      set_perms(fname,file,&st,1);
     }
     return;
   }
 #endif
 
-  if (!S_ISREG(flist->files[i].mode)) {
-    fprintf(stderr,"skipping non-regular file %s\n",fname);
+  if (preserve_hard_links && check_hard_link(file)) {
+    if (verbose > 1)
+      fprintf(FINFO,"%s is a hard link\n",file->name);
+    return;
+  }
+
+  if (!S_ISREG(file->mode)) {
+    fprintf(FERROR,"skipping non-regular file %s\n",fname);
     return;
   }
 
@@ -325,19 +333,19 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out)
       if (!dry_run) send_sums(NULL,f_out);
     } else {
       if (verbose > 1)
-       fprintf(stderr,"recv_generator failed to open %s\n",fname);
+       fprintf(FERROR,"recv_generator failed to open %s\n",fname);
     }
     return;
   }
 
   if (!S_ISREG(st.st_mode)) {
-    fprintf(stderr,"%s : not a regular file\n",fname);
+    fprintf(FERROR,"%s : not a regular file\n",fname);
     return;
   }
 
-  if (update_only && st.st_mtime >= flist->files[i].modtime) {
+  if (update_only && st.st_mtime >= file->modtime) {
     if (verbose > 1)
-      fprintf(stderr,"%s is newer\n",fname);
+      fprintf(FERROR,"%s is newer\n",fname);
     return;
   }
 
@@ -345,11 +353,11 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out)
     file_checksum(fname,sum,st.st_size);
   }
 
-  if (st.st_size == flist->files[i].length &&
-      ((!ignore_times && st.st_mtime == flist->files[i].modtime) ||
+  if (st.st_size == file->length &&
+      ((!ignore_times && st.st_mtime == file->modtime) ||
        (always_checksum && S_ISREG(st.st_mode) &&        
-       memcmp(sum,flist->files[i].sum,csum_length) == 0))) {
-    set_perms(fname,&flist->files[i],&st,1);
+       memcmp(sum,file->sum,csum_length) == 0))) {
+    set_perms(fname,file,&st,1);
     return;
   }
 
@@ -362,7 +370,7 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out)
   fd = open(fname,O_RDONLY);
 
   if (fd == -1) {
-    fprintf(stderr,"failed to open %s : %s\n",fname,strerror(errno));
+    fprintf(FERROR,"failed to open %s : %s\n",fname,strerror(errno));
     return;
   }
 
@@ -373,7 +381,7 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out)
   }
 
   if (verbose > 3)
-    fprintf(stderr,"mapped %s of size %d\n",fname,(int)st.st_size);
+    fprintf(FERROR,"mapped %s of size %d\n",fname,(int)st.st_size);
 
   s = generate_sums(buf,st.st_size,block_size);
 
@@ -402,10 +410,10 @@ static void receive_data(int f_in,char *buf,int fd,char *fname)
   for (i=read_int(f_in); i != 0; i=read_int(f_in)) {
     if (i > 0) {
       if (verbose > 3)
-       fprintf(stderr,"data recv %d at %d\n",i,(int)offset);
+       fprintf(FERROR,"data recv %d at %d\n",i,(int)offset);
 
       if (read_write(f_in,fd,i) != i) {
-       fprintf(stderr,"write failed on %s : %s\n",fname,strerror(errno));
+       fprintf(FERROR,"write failed on %s : %s\n",fname,strerror(errno));
        exit_cleanup(1);
       }
       offset += i;
@@ -417,11 +425,11 @@ static void receive_data(int f_in,char *buf,int fd,char *fname)
        len = remainder;
 
       if (verbose > 3)
-       fprintf(stderr,"chunk[%d] of size %d at %d offset=%d\n",
+       fprintf(FERROR,"chunk[%d] of size %d at %d offset=%d\n",
                i,len,(int)offset2,(int)offset);
 
       if (write_sparse(fd,map_ptr(buf,offset2,len),len) != len) {
-       fprintf(stderr,"write failed on %s : %s\n",fname,strerror(errno));
+       fprintf(FERROR,"write failed on %s : %s\n",fname,strerror(errno));
        exit_cleanup(1);
       }
       offset += len;
@@ -429,7 +437,7 @@ static void receive_data(int f_in,char *buf,int fd,char *fname)
   }
 
   if (offset > 0 && sparse_end(fd) != 0) {
-    fprintf(stderr,"write failed on %s : %s\n",fname,strerror(errno));
+    fprintf(FERROR,"write failed on %s : %s\n",fname,strerror(errno));
     exit_cleanup(1);
   }
 }
@@ -439,16 +447,16 @@ static void delete_one(struct file_struct *f)
 {
   if (!S_ISDIR(f->mode)) {
     if (!dry_run && unlink(f->name) != 0) {
-      fprintf(stderr,"unlink %s : %s\n",f->name,strerror(errno));
+      fprintf(FERROR,"unlink %s : %s\n",f->name,strerror(errno));
     } else if (verbose) {
-      fprintf(stderr,"deleting %s\n",f->name);
+      fprintf(FERROR,"deleting %s\n",f->name);
     }
   } else {    
     if (!dry_run && rmdir(f->name) != 0) {
       if (errno != ENOTEMPTY)
-       fprintf(stderr,"rmdir %s : %s\n",f->name,strerror(errno));
+       fprintf(FERROR,"rmdir %s : %s\n",f->name,strerror(errno));
     } else if (verbose) {
-      fprintf(stderr,"deleting directory %s\n",f->name);      
+      fprintf(FERROR,"deleting directory %s\n",f->name);      
     }
   }
 }
@@ -497,9 +505,10 @@ int recv_files(int f_in,struct file_list *flist,char *local_name)
   char fnametmp[MAXPATHLEN];
   char *buf;
   int i;
+  struct file_struct *file;
 
   if (verbose > 2) {
-    fprintf(stderr,"recv_files(%d) starting\n",flist->count);
+    fprintf(FERROR,"recv_files(%d) starting\n",flist->count);
   }
 
   if (recurse && delete_mode && !local_name && flist->count>0) {
@@ -511,7 +520,8 @@ int recv_files(int f_in,struct file_list *flist,char *local_name)
       i = read_int(f_in);
       if (i == -1) break;
 
-      fname = flist->files[i].name;
+      file = &flist->files[i];
+      fname = file->name;
 
       if (local_name)
        fname = local_name;
@@ -523,19 +533,19 @@ int recv_files(int f_in,struct file_list *flist,char *local_name)
       }
 
       if (verbose > 2)
-       fprintf(stderr,"recv_files(%s)\n",fname);
+       fprintf(FERROR,"recv_files(%s)\n",fname);
 
       /* open the file */  
       fd1 = open(fname,O_RDONLY);
 
       if (fd1 != -1 && fstat(fd1,&st) != 0) {
-       fprintf(stderr,"fstat %s : %s\n",fname,strerror(errno));
+       fprintf(FERROR,"fstat %s : %s\n",fname,strerror(errno));
        close(fd1);
        return -1;
       }
 
       if (fd1 != -1 && !S_ISREG(st.st_mode)) {
-       fprintf(stderr,"%s : not a regular file\n",fname);
+       fprintf(FERROR,"%s : not a regular file\n",fname);
        close(fd1);
        return -1;
       }
@@ -547,17 +557,17 @@ int recv_files(int f_in,struct file_list *flist,char *local_name)
       }
 
       if (verbose > 2)
-       fprintf(stderr,"mapped %s of size %d\n",fname,(int)st.st_size);
+       fprintf(FERROR,"mapped %s of size %d\n",fname,(int)st.st_size);
 
       /* open tmp file */
       sprintf(fnametmp,"%s.XXXXXX",fname);
       if (NULL == mktemp(fnametmp)) {
-       fprintf(stderr,"mktemp %s failed\n",fnametmp);
+       fprintf(FERROR,"mktemp %s failed\n",fnametmp);
        return -1;
       }
-      fd2 = open(fnametmp,O_WRONLY|O_CREAT,flist->files[i].mode);
+      fd2 = open(fnametmp,O_WRONLY|O_CREAT,file->mode);
       if (fd2 == -1) {
-       fprintf(stderr,"open %s : %s\n",fnametmp,strerror(errno));
+       fprintf(FERROR,"open %s : %s\n",fnametmp,strerror(errno));
        return -1;
       }
       
@@ -576,30 +586,30 @@ int recv_files(int f_in,struct file_list *flist,char *local_name)
       close(fd2);
 
       if (verbose > 2)
-       fprintf(stderr,"renaming %s to %s\n",fnametmp,fname);
+       fprintf(FERROR,"renaming %s to %s\n",fnametmp,fname);
 
       if (make_backups) {
        char fnamebak[MAXPATHLEN];
        sprintf(fnamebak,"%s%s",fname,backup_suffix);
        if (rename(fname,fnamebak) != 0 && errno != ENOENT) {
-         fprintf(stderr,"rename %s %s : %s\n",fname,fnamebak,strerror(errno));
+         fprintf(FERROR,"rename %s %s : %s\n",fname,fnamebak,strerror(errno));
          exit_cleanup(1);
        }
       }
 
       /* move tmp file over real file */
       if (rename(fnametmp,fname) != 0) {
-       fprintf(stderr,"rename %s -> %s : %s\n",
+       fprintf(FERROR,"rename %s -> %s : %s\n",
                fnametmp,fname,strerror(errno));
       }
 
       cleanup_fname = NULL;
 
-      set_perms(fname,&flist->files[i],NULL,0);
+      set_perms(fname,file,NULL,0);
     }
 
   if (verbose > 2)
-    fprintf(stderr,"recv_files finished\n");
+    fprintf(FERROR,"recv_files finished\n");
   
   return 0;
 }
@@ -615,9 +625,10 @@ off_t send_files(struct file_list *flist,int f_out,int f_in)
   char fname[MAXPATHLEN];  
   off_t total=0;
   int i;
+  struct file_struct *file;
 
   if (verbose > 2)
-    fprintf(stderr,"send_files starting\n");
+    fprintf(FERROR,"send_files starting\n");
 
   setup_nonblocking(f_in,f_out);
 
@@ -626,15 +637,17 @@ off_t send_files(struct file_list *flist,int f_out,int f_in)
       i = read_int(f_in);
       if (i == -1) break;
 
+      file = &flist->files[i];
+
       fname[0] = 0;
-      if (flist->files[i].dir) {
-       strcpy(fname,flist->files[i].dir);
+      if (file->dir) {
+       strcpy(fname,file->dir);
        strcat(fname,"/");
       }
-      strcat(fname,flist->files[i].name);
+      strcat(fname,file->name);
 
       if (verbose > 2) 
-       fprintf(stderr,"send_files(%d,%s)\n",i,fname);
+       fprintf(FERROR,"send_files(%d,%s)\n",i,fname);
 
       if (dry_run) {   
        if (!am_server && verbose)
@@ -645,20 +658,20 @@ off_t send_files(struct file_list *flist,int f_out,int f_in)
 
       s = receive_sums(f_in);
       if (!s) {
-       fprintf(stderr,"receive_sums failed\n");
+       fprintf(FERROR,"receive_sums failed\n");
        return -1;
       }
 
       fd = open(fname,O_RDONLY);
       if (fd == -1) {
-       fprintf(stderr,"send_files failed to open %s: %s\n",
+       fprintf(FERROR,"send_files failed to open %s: %s\n",
                fname,strerror(errno));
        continue;
       }
   
       /* map the local file */
       if (fstat(fd,&st) != 0) {
-       fprintf(stderr,"fstat failed : %s\n",strerror(errno));
+       fprintf(FERROR,"fstat failed : %s\n",strerror(errno));
        return -1;
       }
       
@@ -669,7 +682,7 @@ off_t send_files(struct file_list *flist,int f_out,int f_in)
       }
 
       if (verbose > 2)
-       fprintf(stderr,"send_files mapped %s of size %d\n",
+       fprintf(FERROR,"send_files mapped %s of size %d\n",
                fname,(int)st.st_size);
 
       write_int(f_out,i);
@@ -679,7 +692,7 @@ off_t send_files(struct file_list *flist,int f_out,int f_in)
       write_int(f_out,s->remainder);
 
       if (verbose > 2)
-       fprintf(stderr,"calling match_sums %s\n",fname);
+       fprintf(FERROR,"calling match_sums %s\n",fname);
 
       if (!am_server && verbose)
        printf("%s\n",fname);
@@ -693,11 +706,14 @@ off_t send_files(struct file_list *flist,int f_out,int f_in)
       free_sums(s);
 
       if (verbose > 2)
-       fprintf(stderr,"sender finished %s\n",fname);
+       fprintf(FERROR,"sender finished %s\n",fname);
 
       total += st.st_size;
     }
 
+  if (verbose > 2)
+    fprintf(FERROR,"send files finished\n");
+
   match_report();
 
   write_int(f_out,-1);
@@ -713,25 +729,28 @@ void generate_files(int f,struct file_list *flist,char *local_name)
   int i;
 
   if (verbose > 2)
-    fprintf(stderr,"generator starting pid=%d count=%d\n",
+    fprintf(FERROR,"generator starting pid=%d count=%d\n",
            (int)getpid(),flist->count);
 
   for (i = 0; i < flist->count; i++) {
-    if (!flist->files[i].name) continue;
-    if (S_ISDIR(flist->files[i].mode)) {
+    struct file_struct *file = &flist->files[i];
+    if (!file->name) continue;
+    if (S_ISDIR(file->mode)) {
       if (dry_run) continue;
-      if (mkdir(flist->files[i].name,flist->files[i].mode) != 0 &&
+      if (mkdir(file->name,file->mode) != 0 &&
          errno != EEXIST) {
-       fprintf(stderr,"mkdir %s : %s\n",
-               flist->files[i].name,strerror(errno));
+       fprintf(FERROR,"mkdir %s : %s\n",
+               file->name,strerror(errno));
       }
       continue;
     }
-    recv_generator(local_name?local_name:flist->files[i].name,
+    recv_generator(local_name?local_name:file->name,
                   flist,i,f);
   }
   write_int(f,-1);
   write_flush(f);
   if (verbose > 2)
-    fprintf(stderr,"generator wrote %d\n",write_total());
+    fprintf(FERROR,"generator wrote %d\n",write_total());
 }
+
+
diff --git a/rsync.h b/rsync.h
index 3220e46..45e1e7f 100644 (file)
--- a/rsync.h
+++ b/rsync.h
@@ -25,7 +25,7 @@
 
 #define FILE_VALID 1
 #define SAME_MODE (1<<1)
-#define SAME_DEV (1<<2)
+#define SAME_RDEV (1<<2)
 #define SAME_UID (1<<3)
 #define SAME_GID (1<<4)
 #define SAME_DIR (1<<5)
 #define MIN_PROTOCOL_VERSION 10
 #define MAX_PROTOCOL_VERSION 20
 
+#define SPARSE_WRITE_SIZE (4*1024)
+#define WRITE_SIZE (32*1024)
 #define CHUNK_SIZE (32*1024)
 #define MAX_MAP_SIZE (4*1024*1024)
 
 #define BLOCKING_TIMEOUT 10
 
-/* do we try to create sparse files */
-#define SPARSE_FILES 1
+#define FERROR stderr
+#define FINFO (am_server?stderr:stdout)
 
 #include "config.h"
 
@@ -175,7 +177,9 @@ struct file_struct {
   time_t modtime;
   off_t length;
   mode_t mode;
+  ino_t inode;
   dev_t dev;
+  dev_t rdev;
   uid_t uid;
   gid_t gid;
   char *name;
@@ -250,6 +254,7 @@ extern int errno;
 #endif
 
 #define SUPPORT_LINKS (HAVE_READLINK && defined(S_ISLNK))
+#define SUPPORT_HARD_LINKS HAVE_LINK
 
 #ifndef S_ISLNK
 #define S_ISLNK(x) 0
diff --git a/util.c b/util.c
index ff40b88..f9985ef 100644 (file)
--- a/util.c
+++ b/util.c
@@ -78,7 +78,7 @@ char *map_ptr(char *buf,off_t offset,int len)
 
   if (lseek(map_fd,offset,SEEK_SET) != offset ||
       read(map_fd,p,len) != len) {
-    fprintf(stderr,"EOF in map_ptr!\n");
+    fprintf(FERROR,"EOF in map_ptr!\n");
     exit_cleanup(1);
   }
 
@@ -110,14 +110,14 @@ int piped_child(char **command,int *f_in,int *f_out)
 
   if (pipe(to_child_pipe) < 0 ||
       pipe(from_child_pipe) < 0) {
-    fprintf(stderr,"pipe: %s\n",strerror(errno));
+    fprintf(FERROR,"pipe: %s\n",strerror(errno));
     exit_cleanup(1);
   }
 
 
   pid = fork();
   if (pid < 0) {
-    fprintf(stderr,"fork: %s\n",strerror(errno));
+    fprintf(FERROR,"fork: %s\n",strerror(errno));
     exit_cleanup(1);
   }
 
@@ -127,18 +127,18 @@ int piped_child(char **command,int *f_in,int *f_out)
          close(to_child_pipe[1]) < 0 ||
          close(from_child_pipe[0]) < 0 ||
          dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
-       fprintf(stderr,"Failed to dup/close : %s\n",strerror(errno));
+       fprintf(FERROR,"Failed to dup/close : %s\n",strerror(errno));
        exit_cleanup(1);
       }
       execvp(command[0], command);
-      fprintf(stderr,"Failed to exec %s : %s\n",
+      fprintf(FERROR,"Failed to exec %s : %s\n",
              command[0],strerror(errno));
       exit_cleanup(1);
     }
 
   if (close(from_child_pipe[1]) < 0 ||
       close(to_child_pipe[0]) < 0) {
-    fprintf(stderr,"Failed to close : %s\n",strerror(errno));   
+    fprintf(FERROR,"Failed to close : %s\n",strerror(errno));   
     exit_cleanup(1);
   }
 
@@ -151,7 +151,7 @@ int piped_child(char **command,int *f_in,int *f_out)
 
 void out_of_memory(char *str)
 {
-  fprintf(stderr,"out of memory in %s\n",str);
+  fprintf(FERROR,"out of memory in %s\n",str);
   exit_cleanup(1);
 }