Support using a "crib tree" for speeding up
diff --git a/mkzftree.1 b/mkzftree.1
index f7b70c0..cedf453 100644
--- a/mkzftree.1
+++ b/mkzftree.1
@@ -50,6 +50,17 @@
 Do not cross filesystem boundaries, and do not create directory stubs
 at mount points.
 .TP
+\fB\-C\fP \fIpath\fP, \fB\-\-crib-path\fP \fIpath\fP
+Steal ("crib") files from another directory if it looks (based on
+name, size, type and modification time) like they match entries in the
+new filesystem.  The "crib tree" is usually the compressed version of
+an older version of the same workload; this thus allows for
+"incremental rebuilds" of a compressed filesystem tree.  The files are
+hardlinked from the crib tree to the output tree, so if it is
+desirable to keep the link count correct the crib path should be
+deleted before running \fBmkisofs\fP.  The crib tree must be on the
+same filesystem as the output tree.
+.TP
 \fB\-l\fP, \fB\-\-local\fP
 Do not recurse into subdirectories, but create the directories
 themselves.
diff --git a/mkzftree.c b/mkzftree.c
index f98904c..8adfdb7 100644
--- a/mkzftree.c
+++ b/mkzftree.c
@@ -103,6 +103,7 @@
   { "parallelism",           1,  0,  'p' },
   { "one-filesystem",        0,  0,  'x' },
   { "strict-one-filesystem", 0,  0,  'X' },
+  { "crib-tree",             1,  0,  'C' },
   { "local",                 0,  0,  'l' },
   { "strict-local",          0,  0,  'L' },
   { "file",                  0,  0,  'F' },
@@ -130,6 +131,7 @@
 	  LO("  --parallelism #        ")"  -p #  Process up to # files in parallel\n"
 	  LO("  --one-filesystem       ")"  -x    Do not cross filesystem boundaries\n"
 	  LO("  --strict-one-filesystem")"  -X    Same as -x, but don't create stubs dirs\n"
+	  LO("  --crib-tree            ")"  -C    Steal \"crib\" files from an old tree\n"
 	  LO("  --local                ")"  -l    Do not recurse into subdirectoires\n"
 	  LO("  --strict-local         ")"  -L    Same as -l, but don't create stubs dirs\n"
 	  LO("  --file                 ")"  -F    Operate possibly on a single file\n"
@@ -158,7 +160,7 @@
 
 int main(int argc, char *argv[])
 {
-  const char *in, *out;
+  const char *in, *out, *crib = NULL;
   struct stat st;
   struct utimbuf ut;
   int optch, err;
@@ -191,6 +193,9 @@
     case 'u':
       opt.munger = block_uncompress_file;
       break;
+    case 'C':
+      crib = optarg;
+      break;
     case 'p':
       opt.parallel = opt_atoi(optarg);
       break;
@@ -238,7 +243,7 @@
       exit(EX_NOINPUT);
     }
 
-    err = munge_entry(in, out, NULL);
+    err = munge_entry(in, out, crib, NULL);
   } else {
     /* Special case: we use stat() for the root, not lstat() */
     if ( stat(in, &st) ) {
@@ -250,7 +255,7 @@
       exit(EX_DATAERR);
     }
     
-    err = munge_tree(in, out);
+    err = munge_tree(in, out, crib);
   }    
 
   wait_for_all_workers();
diff --git a/mkzftree.h b/mkzftree.h
index 0034600..f954e6f 100644
--- a/mkzftree.h
+++ b/mkzftree.h
@@ -49,6 +49,7 @@
   vl_quiet,			/* No messages */
   vl_error,			/* Error messages only */
   vl_filename,			/* Display filenames */
+  vl_crib,			/* Cribbing files */
 };
 #define default_verbosity vl_error
 struct cmdline_options {
@@ -66,8 +67,8 @@
 extern struct cmdline_options opt;
 
 /* walk.c */
-int munge_tree(const char *, const char *);
-int munge_entry(const char *, const char *, const struct stat *);
+int munge_tree(const char *, const char *, const char *);
+int munge_entry(const char *, const char *, const char *, const struct stat *);
 
 /* workers.c */
 void wait_for_all_workers(void);
diff --git a/walk.c b/walk.c
index 137572b..569371f 100644
--- a/walk.c
+++ b/walk.c
@@ -1,7 +1,7 @@
 #ident "$Id$"
 /* ----------------------------------------------------------------------- *
  *   
- *   Copyright 2001 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2001-2002 H. Peter Anvin - All Rights Reserved
  *
  *   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
@@ -28,13 +28,53 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include "mkzftree.h"
+#include "iso9660.h"
 
-static int munge_path(const char *inpath, const char *outpath, struct stat *st)
+static int munge_file(const char *inpath, const char *outpath,
+		      const char *cribpath, struct stat *st)
 {
   FILE *in, *out;
   int err = 0;
   struct utimbuf ut;
 
+  if ( cribpath ) {
+    struct stat cst;
+    struct compressed_file_header cfh;
+
+    /* Compare as much as we realistically can */
+    if ( !stat(cribpath, &cst) &&
+	 st->st_mode == cst.st_mode &&
+	 st->st_uid == cst.st_uid &&
+	 st->st_gid == cst.st_gid &&
+	 st->st_mtime == cst.st_mtime ) {
+      if ( (in = fopen(cribpath, "rb")) ) {
+	int e = fread(&cfh, 1, sizeof cfh, in);
+	fclose(in);
+	/* Attempt to restore the atime */
+	ut.actime  = cst.st_atime;
+	ut.modtime = cst.st_mtime;
+	utime(cribpath, &ut);
+
+	if ( (e == sizeof cfh &&
+	      !memcmp(cfh.magic, zisofs_magic, sizeof zisofs_magic) &&
+	      (off_t)get_731(cfh.uncompressed_len) == st->st_size) ||
+	     (st->st_size == cst.st_size &&
+	      (e < (int)(sizeof zisofs_magic) ||
+	       memcmp(cfh.magic, zisofs_magic, sizeof zisofs_magic))) ) {
+	  /* File is cribbable.  Steal it. */
+	  if ( !link(cribpath, outpath) ) {
+	    message(vl_crib, "crib: %s -> %s\n", cribpath, outpath);
+	    ut.actime  = st->st_atime;
+	    ut.modtime = st->st_mtime;
+	    utime(outpath, &ut);	/* Set the the atime */
+	    
+	    return 0;
+	  }
+	}
+      }
+    }
+  }
+
   in = fopen(inpath, "rb");
   if ( !in )
     return EX_NOINPUT;
@@ -49,19 +89,12 @@
     fclose(in);
     fclose(out);
     
-#ifdef HAVE_LCHOWN
-    lchown(outpath, st->st_uid, st->st_gid);
-#endif
-    if ( !S_ISLNK(st->st_mode) ) {
-#ifndef HAVE_LCHOWN
-      chown(outpath, st->st_uid, st->st_gid);
-#endif
-      chmod(outpath, st->st_mode);
-      ut.actime  = st->st_atime;
-      ut.modtime = st->st_mtime;
-      utime(outpath, &ut);
-    }
-
+    chown(outpath, st->st_uid, st->st_gid);
+    chmod(outpath, st->st_mode);
+    ut.actime  = st->st_atime;
+    ut.modtime = st->st_mtime;
+    utime(outpath, &ut);
+    
     end_worker(err);
   } else {
     fclose(in);
@@ -71,9 +104,10 @@
   return err;
 }
 
-int munge_tree(const char *intree, const char *outtree)
+int munge_tree(const char *intree, const char *outtree, const char *cribtree)
 {
-  char *in_path, *out_path, *in_file, *out_file;
+  char *in_path, *out_path, *crib_path;
+  char *in_file, *out_file, *crib_file;
   DIR *thisdir;
   struct dirent *dirent;
   struct stat dirst;
@@ -82,17 +116,24 @@
   /* Construct buffers with the common filename prefix, and point to the end */
 
   in_path = xmalloc(strlen(intree) + NAME_MAX + 2);
-  out_path = xmalloc(strlen(outtree) + NAME_MAX + 2);
-
   strcpy(in_path, intree);
-  strcpy(out_path, outtree);
-
   in_file = strchr(in_path, '\0');
-  out_file = strchr(out_path, '\0');
-
   *in_file++ = '/';
+
+  out_path = xmalloc(strlen(outtree) + NAME_MAX + 2);
+  strcpy(out_path, outtree);
+  out_file = strchr(out_path, '\0');
   *out_file++ = '/';
 
+  if ( cribtree ) {
+    crib_path = xmalloc(strlen(cribtree) + NAME_MAX + 2);
+    strcpy(crib_path, cribtree);
+    crib_file = strchr(crib_path, '\0');
+    *crib_file++ = '/';
+  } else {
+    crib_path = crib_file = NULL;
+  }
+
   /* Get directory information */
   if ( stat(intree, &dirst) ) {
     message(vl_error, "%s: Failed to stat directory %s: %s\n",
@@ -122,8 +163,10 @@
     
     strcpy(in_file, dirent->d_name);
     strcpy(out_file, dirent->d_name);
+    if ( crib_file )
+      strcpy(crib_file, dirent->d_name);
     
-    err = munge_entry(in_path, out_path, &dirst);
+    err = munge_entry(in_path, out_path, crib_path, &dirst);
     if ( err )
       break;
   }
@@ -136,7 +179,8 @@
 }
 
 
-int munge_entry(const char *in_path, const char *out_path, const struct stat *dirst)
+int munge_entry(const char *in_path, const char *out_path,
+		const char *crib_path, const struct stat *dirst)
 {
   struct stat st;
   struct utimbuf ut;
@@ -165,7 +209,7 @@
 	}
       } else {
 	/* First encounter, compress and enter into hash */
-	if ( (err = munge_path(in_path, out_path, &st)) != 0 ) {
+	if ( (err = munge_file(in_path, out_path, crib_path, &st)) != 0 ) {
 	  message(vl_error, "%s: %s: %s", program, in_path, strerror(errno));
 	  return err;
 	}
@@ -173,7 +217,7 @@
       }
     } else {
       /* Singleton file; no funnies */
-      if ( (err = munge_path(in_path, out_path, &st)) != 0 ) {
+      if ( (err = munge_file(in_path, out_path, crib_path, &st)) != 0 ) {
 	message(vl_error, "%s: %s: %s", program, in_path, strerror(errno));
 	return err;
       }
@@ -182,7 +226,7 @@
     /* Recursion: see recursion */
     if ( !opt.onedir &&
 	 (!opt.onefs || (dirst && dirst->st_dev == st.st_dev)) ) {
-      if ( (err = munge_tree(in_path, out_path)) != 0 )
+      if ( (err = munge_tree(in_path, out_path, crib_path)) != 0 )
 	return err;
     } else if ( opt.do_mkdir ) {
       /* Create stub directories */
@@ -244,7 +288,7 @@
     }
   }
   
-  /* This is done by munge_path() for files */
+  /* This is done by munge_file() for files */
   if ( !S_ISREG(st.st_mode) ) {
 #ifdef HAVE_LCHOWN
     if ( lchown(out_path, st.st_uid, st.st_gid) && opt.sloppy && !err ) {