The patches for 3.0.0pre6.
[rsync/rsync-patches.git] / checksum4mirrors.diff
diff --git a/checksum4mirrors.diff b/checksum4mirrors.diff
deleted file mode 100644 (file)
index dd39026..0000000
+++ /dev/null
@@ -1,501 +0,0 @@
-Optimize the ability of a mirror to send checksums.
-
-This adds a sender optimization feature that allows a cache of checksums
-to be used when the client specifies the --checksum option.  The checksum
-files (.rsyncsums) must be created by some other process (see the perl
-script in the support dir for one way).
-
-This option should be used by mirrors that contain files that get created and
-not changed.  There is a minimal amount of sanity-check information in the
-.rsyncsums file (size and mtime) so that the sum files can be shared with your
-mirror network.
-
-To use this patch, run these commands for a successful build:
-
-    patch -p1 <patches/checksum4mirrors.diff
-    ./configure                               (optional if already run)
-    make
-
---- old/flist.c
-+++ new/flist.c
-@@ -117,6 +117,7 @@ static char empty_sum[MAX_DIGEST_LEN];
- static int flist_count_offset; /* for --delete --progress */
- static int dir_count = 0;
- static int high_hlink_ndx;
-+static struct file_list *checksum_flist = NULL;
- static void clean_flist(struct file_list *flist, int strip_root);
- static void output_flist(struct file_list *flist);
-@@ -304,6 +305,186 @@ static void flist_done_allocating(struct
-               flist->pool_boundary = ptr;
- }
-+/* The len count is the length of the basename + 1 for the null. */
-+static int add_checksum(const char *dirname, const char *basename, int len,
-+                      OFF_T file_length, time_t mtime, const char *sum)
-+{
-+      struct file_struct *file;
-+      int alloc_len, extra_len;
-+      char *bp;
-+
-+      if (len == 10+1 && *basename == '.' && strcmp(basename, ".rsyncsums") == 0)
-+              return 0;
-+      if (file_length == 0)
-+              return 0;
-+
-+      extra_len = (file_extra_cnt + (file_length > 0xFFFFFFFFu) + SUM_EXTRA_CNT)
-+                * EXTRA_LEN;
-+#if EXTRA_ROUNDING > 0
-+      if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
-+              extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
-+#endif
-+      alloc_len = FILE_STRUCT_LEN + extra_len + len;
-+      bp = pool_alloc(checksum_flist->file_pool, alloc_len, "add_checksum");
-+
-+      memset(bp, 0, extra_len + FILE_STRUCT_LEN);
-+      bp += extra_len;
-+      file = (struct file_struct *)bp;
-+      bp += FILE_STRUCT_LEN;
-+
-+      memcpy(bp, basename, len);
-+
-+      file->mode = S_IFREG;
-+      file->modtime = mtime;
-+      file->len32 = (uint32)file_length;
-+      if (file_length > 0xFFFFFFFFu) {
-+              file->flags |= FLAG_LENGTH64;
-+              OPT_EXTRA(file, 0)->unum = (uint32)(file_length >> 32);
-+      }
-+      file->dirname = dirname;
-+      bp = F_SUM(file);
-+      memcpy(bp, sum, checksum_len);
-+
-+      flist_expand(checksum_flist, 1);
-+      checksum_flist->files[checksum_flist->used++] = file;
-+
-+      checksum_flist->sorted = checksum_flist->files;
-+
-+      return 1;
-+}
-+
-+/* The direname value must remain unchanged during the lifespan of the
-+ * created checksum_flist object because we use it directly. */
-+static void read_checksums(const char *dirname)
-+{
-+      char line[MAXPATHLEN+1024], fbuf[MAXPATHLEN], sum[MAX_DIGEST_LEN];
-+      OFF_T file_length;
-+      time_t mtime;
-+      int len, dlen, i;
-+      char *cp;
-+      FILE *fp;
-+
-+      if (checksum_flist) {
-+              /* Reset the pool memory and empty the file-list array. */
-+              pool_free_old(checksum_flist->file_pool,
-+                            pool_boundary(checksum_flist->file_pool, 0));
-+              checksum_flist->used = 0;
-+      } else
-+              checksum_flist = flist_new(FLIST_TEMP, "read_checksums");
-+
-+      checksum_flist->low = 0;
-+      checksum_flist->high = -1;
-+
-+      if (!dirname)
-+              return;
-+
-+      dlen = strlcpy(fbuf, dirname, sizeof fbuf);
-+      if (dlen >= (int)sizeof fbuf)
-+              return;
-+      if (dlen)
-+              fbuf[dlen++] = '/';
-+      else
-+              dirname = NULL;
-+      strlcpy(fbuf+dlen, ".rsyncsums", sizeof fbuf - dlen);
-+      if (!(fp = fopen(fbuf, "r")))
-+              return;
-+
-+      while (fgets(line, sizeof line, fp)) {
-+              cp = line;
-+              if (protocol_version >= 30) {
-+                      char *alt_sum = cp;
-+                      if (*cp == '=')
-+                              while (*++cp == '=') {}
-+                      else
-+                              while (isXDigit(cp)) cp++;
-+                      if (cp - alt_sum != MD4_DIGEST_LEN*2 || *cp != ' ')
-+                              break;
-+                      while (*++cp == ' ') {}
-+              }
-+
-+              if (*cp == '=') {
-+                      continue;
-+              } else {
-+                      for (i = 0; i < checksum_len*2; i++, cp++) {
-+                              int x;
-+                              if (isXDigit(cp)) {
-+                                      if (isDigit(cp))
-+                                              x = *cp - '0';
-+                                      else
-+                                              x = (*cp & 0xF) + 9;
-+                              } else {
-+                                      cp = "";
-+                                      break;
-+                              }
-+                              if (i & 1)
-+                                      sum[i/2] |= x;
-+                              else
-+                                      sum[i/2] = x << 4;
-+                      }
-+              }
-+              if (*cp != ' ')
-+                      break;
-+              while (*++cp == ' ') {}
-+
-+              if (protocol_version < 30) {
-+                      char *alt_sum = cp;
-+                      if (*cp == '=')
-+                              while (*++cp == '=') {}
-+                      else
-+                              while (isXDigit(cp)) cp++;
-+                      if (cp - alt_sum != MD5_DIGEST_LEN*2 || *cp != ' ')
-+                              break;
-+                      while (*++cp == ' ') {}
-+              }
-+
-+              file_length = 0;
-+              while (isDigit(cp))
-+                      file_length = file_length * 10 + *cp++ - '0';
-+              if (*cp != ' ')
-+                      break;
-+              while (*++cp == ' ') {}
-+
-+              mtime = 0;
-+              while (isDigit(cp))
-+                      mtime = mtime * 10 + *cp++ - '0';
-+              if (*cp != ' ')
-+                      break;
-+              while (*++cp == ' ') {}
-+
-+              /* Ignore ctime. */
-+              while (isDigit(cp))
-+                      cp++;
-+              if (*cp != ' ')
-+                      break;
-+              while (*++cp == ' ') {}
-+
-+              /* Ignore inode. */
-+              while (isDigit(cp))
-+                      cp++;
-+              if (*cp != ' ')
-+                      break;
-+              while (*++cp == ' ') {}
-+
-+              len = strlen(cp);
-+              while (len && (cp[len-1] == '\n' || cp[len-1] == '\r'))
-+                      len--;
-+              if (!len)
-+                      break;
-+              cp[len++] = '\0'; /* len now counts the null */
-+              if (strchr(cp, '/'))
-+                      break;
-+              if (len > MAXPATHLEN)
-+                      continue;
-+
-+              strlcpy(fbuf+dlen, cp, sizeof fbuf - dlen);
-+
-+              add_checksum(dirname, cp, len, file_length, mtime, sum);
-+      }
-+      fclose(fp);
-+
-+      clean_flist(checksum_flist, 0);
-+}
-+
- int push_pathname(const char *dir, int len)
- {
-       if (dir == pathname)
-@@ -989,7 +1170,7 @@ struct file_struct *make_file(const char
-                             STRUCT_STAT *stp, int flags, int filter_level)
- {
-       static char *lastdir;
--      static int lastdir_len = -1;
-+      static int lastdir_len = -2;
-       struct file_struct *file;
-       char thisname[MAXPATHLEN];
-       char linkname[MAXPATHLEN];
-@@ -1119,9 +1300,16 @@ struct file_struct *make_file(const char
-                       memcpy(lastdir, thisname, len);
-                       lastdir[len] = '\0';
-                       lastdir_len = len;
-+                      if (always_checksum && am_sender && flist)
-+                              read_checksums(lastdir);
-               }
--      } else
-+      } else {
-               basename = thisname;
-+              if (always_checksum && am_sender && flist && lastdir_len == -2) {
-+                      lastdir_len = -1;
-+                      read_checksums("");
-+              }
-+      }
-       basename_len = strlen(basename) + 1; /* count the '\0' */
- #ifdef SUPPORT_LINKS
-@@ -1197,11 +1385,21 @@ struct file_struct *make_file(const char
-       }
- #endif
--      if (always_checksum && am_sender && S_ISREG(st.st_mode))
--              file_checksum(thisname, tmp_sum, st.st_size);
--
-       F_PATHNAME(file) = pathname;
-+      if (always_checksum && am_sender && S_ISREG(st.st_mode)) {
-+              int j;
-+              if (flist && (j = flist_find(checksum_flist, file)) >= 0) {
-+                      struct file_struct *fp = checksum_flist->sorted[j];
-+                      if (F_LENGTH(fp) == st.st_size
-+                       && fp->modtime == st.st_mtime)
-+                              memcpy(tmp_sum, F_SUM(fp), MAX_DIGEST_LEN);
-+                      else
-+                              file_checksum(thisname, tmp_sum, st.st_size);
-+              } else
-+                      file_checksum(thisname, tmp_sum, st.st_size);
-+      }
-+
-       /* This code is only used by the receiver when it is building
-        * a list of files for a delete pass. */
-       if (keep_dirlinks && linkname_len && flist) {
-@@ -2051,7 +2249,11 @@ struct file_list *send_file_list(int f, 
-                        * file-list to check if this is a 1-file xfer. */
-                       send_extra_file_list(f, 1);
-               }
--      }
-+      } else
-+              flist_eof = 1;
-+      
-+      if (checksum_updating && always_checksum && flist_eof)
-+              read_checksums(NULL);
-       return flist;
- }
---- old/ifuncs.h
-+++ new/ifuncs.h
-@@ -64,6 +64,12 @@ isDigit(const char *ptr)
- }
- static inline int
-+isXDigit(const char *ptr)
-+{
-+      return isxdigit(*(unsigned char *)ptr);
-+}
-+
-+static inline int
- isPrint(const char *ptr)
- {
-       return isprint(*(unsigned char *)ptr);
---- old/support/rsyncsums
-+++ new/support/rsyncsums
-@@ -0,0 +1,203 @@
-+#!/usr/bin/perl -w
-+use strict;
-+
-+use Getopt::Long;
-+use Cwd qw(abs_path cwd);
-+use Digest::MD4;
-+use Digest::MD5;
-+
-+our $SUMS_FILE = '.rsyncsums';
-+
-+&Getopt::Long::Configure('bundling');
-+&usage if !&GetOptions(
-+    'recurse|r' => \( my $recurse_opt ),
-+    'simple-cmp|s' => \( my $ignore_ctime_and_inode ),
-+    'check|c' => \( my $check_opt ),
-+    'verbose|v+' => \( my $verbosity = 0 ),
-+    'help|h' => \( my $help_opt ),
-+);
-+&usage if $help_opt;
-+
-+my $start_dir = cwd();
-+
-+my @dirs = @ARGV;
-+@dirs = '.' unless @dirs;
-+foreach (@dirs) {
-+    $_ = abs_path($_);
-+}
-+
-+$| = 1;
-+
-+my $exit_code = 0;
-+
-+my $md4 = Digest::MD4->new;
-+my $md5 = Digest::MD5->new;
-+
-+while (@dirs) {
-+    my $dir = shift @dirs;
-+
-+    if (!chdir($dir)) {
-+      warn "Unable to chdir to $dir: $!\n";
-+      next;
-+    }
-+    if (!opendir(DP, '.')) {
-+      warn "Unable to opendir $dir: $!\n";
-+      next;
-+    }
-+
-+    my $reldir = $dir;
-+    $reldir =~ s#^$start_dir(/|$)# $1 ? '' : '.' #eo;
-+    if ($verbosity) {
-+      print "$reldir ... ";
-+      print "\n" if $check_opt;
-+    }
-+
-+    my %cache;
-+    my $f_cnt = 0;
-+    if (open(FP, '<', $SUMS_FILE)) {
-+      while (<FP>) {
-+          chomp;
-+          my($sum4, $sum5, $size, $mtime, $ctime, $inode, $fn) = split(' ', $_, 7);
-+          $cache{$fn} = [ 0, $sum4, $sum5, $size, $mtime, $ctime & 0xFFFFFFFF, $inode & 0xFFFFFFFF ];
-+          $f_cnt++;
-+      }
-+      close FP;
-+    }
-+
-+    my @subdirs;
-+    my $d_cnt = 0;
-+    my $update_cnt = 0;
-+    while (defined(my $fn = readdir(DP))) {
-+      next if $fn =~ /^\.\.?$/ || $fn =~ /^\Q$SUMS_FILE\E$/o || -l $fn;
-+      if (-d _) {
-+          push(@subdirs, "$dir/$fn") unless $fn =~ /^(CVS|\.svn|\.git|\.bzr)$/;
-+          next;
-+      }
-+      next unless -f _;
-+
-+      my($size,$mtime,$ctime,$inode) = (stat(_))[7,9,10,1];
-+      my $ref = $cache{$fn};
-+      if ($size == 0) {
-+          if (defined $ref) {
-+              delete $cache{$fn};
-+              $f_cnt--;
-+              if (!$check_opt && !$update_cnt++) {
-+                  print "UPDATING\n" if $verbosity;
-+              }
-+          }
-+          next;
-+      }
-+      $d_cnt++;
-+
-+      if (!$check_opt) {
-+          if (defined $ref) {
-+              $$ref[0] = 1;
-+              if ($$ref[3] == $size
-+               && $$ref[4] == $mtime
-+               && ($ignore_ctime_and_inode || ($$ref[5] == $ctime && $$ref[6] == $inode))
-+               && $$ref[1] !~ /=/ && $$ref[2] !~ /=/) {
-+                  next;
-+              }
-+          }
-+          if (!$update_cnt++) {
-+              print "UPDATING\n" if $verbosity;
-+          }
-+      }
-+
-+      if (!open(IN, $fn)) {
-+          print STDERR "Unable to read $fn: $!\n";
-+          if (defined $ref) {
-+              delete $cache{$fn};
-+              $f_cnt--;
-+          }
-+          next;
-+      }
-+
-+      my($sum4, $sum5);
-+      while (1) {
-+          while (sysread(IN, $_, 64*1024)) {
-+              $md4->add($_);
-+              $md5->add($_);
-+          }
-+          $sum4 = $md4->hexdigest;
-+          $sum5 = $md5->hexdigest;
-+          print " $sum4 $sum5" if $verbosity > 2;
-+          print " $fn" if $verbosity > 1;
-+          my($size2,$mtime2,$ctime2,$inode2) = (stat(IN))[7,9,10,1];
-+          last if $size == $size2 && $mtime == $mtime2
-+           && ($ignore_ctime_and_inode || ($ctime == $ctime2 && $inode == $inode2));
-+          $size = $size2;
-+          $mtime = $mtime2;
-+          $ctime = $ctime2;
-+          $inode = $inode2;
-+          sysseek(IN, 0, 0);
-+          print " REREADING\n" if $verbosity > 1;
-+      }
-+
-+      close IN;
-+
-+      if ($check_opt) {
-+          my $dif;
-+          if (!defined $ref) {
-+              $dif = 'MISSING';
-+          } elsif ($sum4 ne $$ref[1] || $sum5 ne $$ref[2]) {
-+              $dif = 'FAILED';
-+          } else {
-+              print " OK\n" if $verbosity > 1;
-+              next;
-+          }
-+          if ($verbosity < 2) {
-+              print $verbosity ? ' ' : "$reldir/";
-+              print $fn;
-+          }
-+          print " $dif\n";
-+          $exit_code = 1;
-+      } else {
-+          print "\n" if $verbosity > 1;
-+          $cache{$fn} = [ 1, $sum4, $sum5, $size, $mtime, $ctime & 0xFFFFFFFF, $inode & 0xFFFFFFFF ];
-+      }
-+    }
-+
-+    closedir DP;
-+
-+    unshift(@dirs, sort @subdirs) if $recurse_opt;
-+
-+    if ($check_opt) {
-+      ;
-+    } elsif ($d_cnt == 0) {
-+      if ($f_cnt) {
-+          print "(removed $SUMS_FILE) " if $verbosity;
-+          unlink($SUMS_FILE);
-+      }
-+      print "empty\n" if $verbosity;
-+    } elsif ($update_cnt || $d_cnt != $f_cnt) {
-+      print "UPDATING\n" if $verbosity && !$update_cnt;
-+      open(FP, '>', $SUMS_FILE) or die "Unable to write $dir/$SUMS_FILE: $!\n";
-+
-+      foreach my $fn (sort keys %cache) {
-+          my $ref = $cache{$fn};
-+          my($found, $sum4, $sum5, $size, $mtime, $ctime, $inode) = @$ref;
-+          next unless $found;
-+          printf FP '%s %s %10d %10d %10d %10d %s' . "\n", $sum4, $sum5, $size, $mtime, $ctime, $inode, $fn;
-+      }
-+      close FP;
-+    } else {
-+      print "ok\n" if $verbosity;
-+    }
-+}
-+
-+exit $exit_code;
-+
-+sub usage
-+{
-+    die <<EOT;
-+Usage: rsyncsums [OPTIONS] [DIRS]
-+
-+Options:
-+ -r, --recurse     Update $SUMS_FILE files in subdirectories too.
-+ -s, --simple-cmp  Ignore ctime and inode values when comparing identicality.
-+ -c, --check       Check if the checksums are right (doesn't update).
-+ -v, --verbose     Mention what we're doing.  Repeat for more info.
-+ -h, --help        Display this help message.
-+EOT
-+}