Separate the MD5 code into its own module.
authorMatthew Dillon <dillon@dragonflybsd.org>
Tue, 25 Apr 2006 21:30:45 +0000 (21:30 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Tue, 25 Apr 2006 21:30:45 +0000 (21:30 +0000)
Add -k/-K to have cpdup use the experimental FSMID to detect changes,
allowing it to shortcut whole directory subhiearchies.

It should be noted that using cpdup with a NFS or other remote filesystem
as the source with this option will not work, since the NFS client cannot
currently detect if the server (or some other client) has modified a file
or directory.

bin/cpdup/Makefile
bin/cpdup/cpdup.1
bin/cpdup/cpdup.c
bin/cpdup/cpdup.h
bin/cpdup/fsmid.c [new file with mode: 0644]
bin/cpdup/md5.c [new file with mode: 0644]
bin/cpdup/misc.c

index 2dd37e9..f7faf63 100644 (file)
@@ -1,8 +1,8 @@
-# $DragonFly: src/bin/cpdup/Makefile,v 1.4 2005/02/06 06:16:39 okumoto Exp $
+# $DragonFly: src/bin/cpdup/Makefile,v 1.5 2006/04/25 21:30:45 dillon Exp $
 #
 
 PROG=  cpdup
-SRCS=  cpdup.c misc.c
+SRCS=  cpdup.c misc.c md5.c fsmid.c
 
 LDADD+= -lmd
 DPADD+= ${LIBMD}
index 966cbd6..47d3e9a 100644 (file)
@@ -3,7 +3,7 @@
 .\"    USE WITH EXTREME CAUTION.
 .\"
 .\"
-.\" $DragonFly: src/bin/cpdup/cpdup.1,v 1.8 2005/08/01 01:49:16 swildner Exp $
+.\" $DragonFly: src/bin/cpdup/cpdup.1,v 1.9 2006/04/25 21:30:45 dillon Exp $
 .Dd October 28, 1999
 .Dt CPDUP 1
 .Os BSD 4
 .Fl M
 .Ar file
 .Oc
+.Op Fl k
+.Oo
+.Fl K
+.Ar file
+.Oc
 .Oo
 .Fl X
 .Ar file
@@ -83,7 +88,7 @@ Quiet operation
 .It Fl o
 Do not remove any files, just overwrite/add.
 .It Fl m
-Generate and maintain an MD5 checkfile in each directory on the source
+Generate and maintain a MD5 checkfile in each directory on the source
 and do an MD5 check on each file of the destination when the destination
 appears to be the same as the source.  If the check fails,
 .Nm
@@ -93,10 +98,34 @@ even if modifications are made to a source file.  If you do not specify a
 destination directory the
 .Nm
 command forcefully regenerates the MD5 checkfile for every file in the source.
-.It Fl M
+.It Fl M Ar file
 Works the same as
 .Fl m
 but allows you to specify the name of the MD5 checkfile.
+.It Fl k
+Generate and maintain a FSMID checkfile called .FSMID.CHECK in each
+directory on the target.
+.Nm
+will check the FSMID for each source file or directory against the checkfile
+on the target and will not copy the file or recurse through the directory
+when a match occurs.  Any source file or directory with the same name as the
+checkfile will be ignored.  The FSMID will be re-checked after the copy
+has been completed and
+.Nm
+will loop on that directory or file until it is sure it has an exact copy. 
+.Pp
+Warning: FSMID is not always supported by a filesystem and may not be 
+synchronized if a crash occurs.  DragonFly will simulate an FSMID when
+it is otherwise not supported by the filesystem, and users should be aware
+that simulated FSMIDs may change state in such cases even if the underlying
+hierarchy does not due to cache flushes. 
+Additionally, the FSMID may not reflect changes made to remote filesystems
+by other hosts.  For example, using these options with NFS mounted sources
+will not work well.
+.It Fl K Ar file
+Works the same as 
+.Fl k
+but allows you to specify the name of the FSMID checkfile.
 .It Fl x
 Causes
 .Nm
@@ -105,7 +134,7 @@ determine which files to ignore.  When this option is used, the exclusion
 filename itself is automatically excluded from the copy.  If this option is
 not used then the filename ".cpignore" is not considered special and will
 be copied along with everything else.
-.It Fl X
+.It Fl X Ar file
 Works the same as
 .Fl x
 but allows you to specify the name of the exclusion file.  This file is
index b0bca21..6f969b4 100644 (file)
@@ -42,7 +42,7 @@
  *
  *     - Can do MD5 consistancy checks
  *
- * $DragonFly: src/bin/cpdup/cpdup.c,v 1.9 2005/10/15 19:09:17 swildner Exp $
+ * $DragonFly: src/bin/cpdup/cpdup.c,v 1.10 2006/04/25 21:30:45 dillon Exp $
  */
 
 /*-
@@ -58,8 +58,6 @@
 #define HMASK  (HSIZE-1)
 #define HASHF 16
 
-const char *MD5CacheFile;
-
 typedef struct Node {
     struct Node *no_Next;
     struct Node *no_HNext;
@@ -83,14 +81,6 @@ struct hlink {
 
 struct hlink *hltable[HASHF];
 
-typedef struct MD5Node {
-    struct MD5Node *md_Next;
-    char *md_Name;
-    char *md_Code;
-    int md_Accessed;
-} MD5Node;
-
-
 void RemoveRecur(const char *dpath, dev_t devNo);
 void InitList(List *list);
 void ResetList(List *list);
@@ -103,35 +93,28 @@ int YesNo(const char *path);
 static int xrename(const char *src, const char *dst, u_long flags);
 static int xlink(const char *src, const char *dst, u_long flags);
 int WildCmp(const char *s1, const char *s2);
-static MD5Node *md5_lookup(const char *sfile);
-static int md5_check(const char *spath, const char *dpath);
-static void md5_flush(void);
-static void md5_cache(const char *spath, int sdirlen);
-static char *fextract(FILE *fi, int n, int *pc, int skip);
 int DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo);
-char *doMD5File(const char *filename, char *buf);
 
 int AskConfirmation = 1;
 int SafetyOpt = 1;
-int ForceOpt = 0;
-int VerboseOpt = 0;
-int QuietOpt = 0;
-int NoRemoveOpt = 0;
-int UseMD5Opt = 0;
-int SummaryOpt = 0;
+int ForceOpt;
+int VerboseOpt;
+int QuietOpt;
+int NoRemoveOpt;
+int UseMD5Opt;
+int UseFSMIDOpt;
+int SummaryOpt;
+int EnableDirectoryRetries;
 const char *UseCpFile;
+const char *MD5CacheFile;
+const char *FSMIDCacheFile;
 
-int64_t CountSourceBytes = 0;
-int64_t CountSourceItems = 0;
-int64_t CountCopiedItems = 0;
-int64_t CountReadBytes = 0;
-int64_t CountWriteBytes = 0;
-int64_t CountRemovedItems = 0;
-
-char *MD5SCache;               /* cache source directory name */
-MD5Node *MD5Base;
-int MD5SCacheDirLen;
-int MD5SCacheDirty;
+int64_t CountSourceBytes;
+int64_t CountSourceItems;
+int64_t CountCopiedItems;
+int64_t CountReadBytes;
+int64_t CountWriteBytes;
+int64_t CountRemovedItems;
 
 int
 main(int ac, char **av)
@@ -196,6 +179,14 @@ main(int ac, char **av)
        case 'q':
            QuietOpt = v;
            break;
+       case 'k':
+           UseFSMIDOpt = v;
+           FSMIDCacheFile = ".FSMID.CHECK";
+           break;
+       case 'K':
+           UseFSMIDOpt = v;
+           FSMIDCacheFile = av[++i];
+           break;
        case 'M':
            UseMD5Opt = v;
            MD5CacheFile = av[++i];
@@ -229,6 +220,7 @@ main(int ac, char **av)
        i = DoCopy(src, NULL, (dev_t)-1, (dev_t)-1);
     }
     md5_flush();
+    fsmid_flush();
 
     if (SummaryOpt && i == 0) {
        long duration;
@@ -325,14 +317,13 @@ DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo)
 {
     struct stat st1;
     struct stat st2;
-    int r, mres, st2Valid;
+    int r, mres, fres, st2Valid;
     struct hlink *hln;
     List list;
     u_int64_t size;
 
     InitList(&list);
-
-    r = mres = st2Valid = 0;
+    r = mres = fres = st2Valid = 0;
     size = 0;
     hln = NULL;
 
@@ -415,7 +406,8 @@ DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo)
 
     /*
      * Do we need to copy the file/dir/link/whatever?  Early termination
-     * if we do not.  Always traverse directories.  Always redo links.
+     * if we do not.  Always redo links.  Directories are always traversed
+     * except when the FSMID options are used.
      *
      * NOTE: st2Valid is true only if dpath != NULL *and* dpath stats good.
      */
@@ -426,7 +418,23 @@ DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo)
        st1.st_flags == st2.st_flags
     ) {
        if (S_ISLNK(st1.st_mode) || S_ISDIR(st1.st_mode)) {
-           ;
+           /*
+            * If FSMID tracking is turned on we can avoid recursing through
+            * an entire directory subtree if the FSMID matches.
+            */
+#ifdef _ST_FSMID_PRESENT_
+           if (ForceOpt == 0 &&
+               (UseFSMIDOpt && (fres = fsmid_check(st1.st_fsmid, dpath)) == 0)
+           ) {
+               if (VerboseOpt >= 3) {
+                   if (UseFSMIDOpt)
+                       logstd("%-32s fsmid-nochange\n", (dpath ? dpath : spath));
+                   else
+                       logstd("%-32s nochange\n", (dpath ? dpath : spath));
+               }
+               return(0);
+           }
+#endif
        } else {
            if (ForceOpt == 0 &&
                st1.st_size == st2.st_size &&
@@ -434,12 +442,17 @@ DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo)
                st1.st_gid == st2.st_gid &&
                st1.st_mtime == st2.st_mtime
                && (UseMD5Opt == 0 || (mres = md5_check(spath, dpath)) == 0)
+#ifdef _ST_FSMID_PRESENT_
+               && (UseFSMIDOpt == 0 || (fres = fsmid_check(st1.st_fsmid, dpath)) == 0)
+#endif
            ) {
                 if (hln)
                     hln->dino = st2.st_ino;
                if (VerboseOpt >= 3) {
                    if (UseMD5Opt)
                        logstd("%-32s md5-nochange\n", (dpath ? dpath : spath));
+                   else if (UseFSMIDOpt)
+                       logstd("%-32s fsmid-nochange\n", (dpath ? dpath : spath));
                    else
                        logstd("%-32s nochange\n", (dpath ? dpath : spath));
                }
@@ -467,9 +480,14 @@ DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo)
            RemoveRecur(dpath, ddevNo);
     }
 
+    /*
+     * The various comparisons failed, copy it.
+     */
     if (S_ISDIR(st1.st_mode)) {
        DIR *dir;
 
+       if (fres < 0)
+           logerr("%-32s/ fsmid-CHECK-FAILED\n", (dpath) ? dpath : spath);
        if ((dir = opendir(spath)) != NULL) {
            struct dirent *den;
            int noLoop = 0;
@@ -550,9 +568,14 @@ DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo)
            /*
             * Automatically exclude MD5CacheFile that we create on the
             * source from the copy to the destination.
+            *
+            * Automatically exclude a FSMIDCacheFile on the source that
+            * would otherwise overwrite the one we maintain on the target.
             */
            if (UseMD5Opt)
                AddList(&list, MD5CacheFile, 1);
+           if (UseFSMIDOpt)
+               AddList(&list, FSMIDCacheFile, 1);
 
            while (noLoop == 0 && (den = readdir(dir)) != NULL) {
                /*
@@ -661,6 +684,8 @@ DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo)
         */
        if (mres < 0)
            logerr("%-32s md5-CHECK-FAILED\n", (dpath) ? dpath : spath);
+       else if (fres < 0)
+           logerr("%-32s fsmid-CHECK-FAILED\n", (dpath) ? dpath : spath);
 
        if ((fd1 = open(spath, O_RDONLY)) >= 0) {
            if ((fd2 = open(path, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
@@ -836,7 +861,7 @@ DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo)
        CountSourceItems++;
     }
     ResetList(&list);
-    return(r);
+    return (r);
 }
 
 /*
@@ -1072,235 +1097,6 @@ YesNo(const char *path)
     return ((first == 'y' || first == 'Y'));
 }
 
-static void 
-md5_flush(void)
-{
-    if (MD5SCacheDirty && MD5SCache) {
-       FILE *fo;
-
-       if ((fo = fopen(MD5SCache, "w")) != NULL) {
-           MD5Node *node;
-
-           for (node = MD5Base; node; node = node->md_Next) {
-               if (node->md_Accessed && node->md_Code) {
-                   fprintf(fo, "%s %d %s\n", 
-                       node->md_Code, 
-                       strlen(node->md_Name),
-                       node->md_Name
-                   );
-               }
-           }
-           fclose(fo);
-       }
-    }
-
-    MD5SCacheDirty = 0;
-
-    if (MD5SCache) {
-       MD5Node *node;
-
-       while ((node = MD5Base) != NULL) {
-           MD5Base = node->md_Next;
-
-           if (node->md_Code)
-               free(node->md_Code);
-           if (node->md_Name)
-               free(node->md_Name);
-           free(node);
-       }
-       free(MD5SCache);
-       MD5SCache = NULL;
-    }
-}
-
-static void
-md5_cache(const char *spath, int sdirlen)
-{
-    FILE *fi;
-
-    /*
-     * Already cached
-     */
-
-    if (
-       MD5SCache &&
-       sdirlen == MD5SCacheDirLen &&
-       strncmp(spath, MD5SCache, sdirlen) == 0
-    ) {
-       return;
-    }
-
-    /*
-     * Different cache, flush old cache
-     */
-
-    if (MD5SCache != NULL)
-       md5_flush();
-
-    /*
-     * Create new cache
-     */
-
-    MD5SCacheDirLen = sdirlen;
-    MD5SCache = mprintf("%*.*s%s", sdirlen, sdirlen, spath, MD5CacheFile);
-
-    if ((fi = fopen(MD5SCache, "r")) != NULL) {
-       MD5Node **pnode = &MD5Base;
-       int c;
-
-       c = fgetc(fi);
-       while (c != EOF) {
-           MD5Node *node = *pnode = malloc(sizeof(MD5Node));
-           char *s;
-           int nlen;
-
-           nlen = 0;
-
-           if (pnode == NULL || node == NULL) {
-               fprintf(stderr, "out of memory\n");
-               exit(EXIT_FAILURE);
-           }
-
-           bzero(node, sizeof(MD5Node));
-           node->md_Code = fextract(fi, -1, &c, ' ');
-           node->md_Accessed = 1;
-           if ((s = fextract(fi, -1, &c, ' ')) != NULL) {
-               nlen = strtol(s, NULL, 0);
-               free(s);
-           }
-           /*
-            * extracting md_Name - name may contain embedded control 
-            * characters.
-            */
-           CountReadBytes += nlen+1;
-           node->md_Name = fextract(fi, nlen, &c, EOF);
-           if (c != '\n') {
-               fprintf(stderr, "Error parsing MD5 Cache: %s (%c)\n", MD5SCache, c);
-               while (c != EOF && c != '\n')
-                   c = fgetc(fi);
-           }
-           if (c != EOF)
-               c = fgetc(fi);
-           pnode = &node->md_Next;
-       }
-       fclose(fi);
-    }
-}
-
-/*
- * md5_lookup: lookup/create md5 entry
- */
-
-static MD5Node *
-md5_lookup(const char *sfile)
-{
-    MD5Node **pnode;
-    MD5Node *node;
-
-    for (pnode = &MD5Base; (node = *pnode) != NULL; pnode = &node->md_Next) {
-       if (strcmp(sfile, node->md_Name) == 0) {
-           break;
-       }
-    }
-    if (node == NULL) {
-
-       if ((node = *pnode = malloc(sizeof(MD5Node))) == NULL) {
-               fprintf(stderr,"out of memory\n");
-               exit(EXIT_FAILURE);
-       }
-
-       bzero(node, sizeof(MD5Node));
-       node->md_Name = strdup(sfile);
-    }
-    node->md_Accessed = 1;
-    return(node);
-}
-
-/*
- * md5_check:  check MD5 against file
- *
- *     Return -1 if check failed
- *     Return 0  if check succeeded
- *
- * dpath can be NULL, in which case we are force-updating
- * the source MD5.
- */
-
-static int
-md5_check(const char *spath, const char *dpath)
-{
-    const char *sfile;
-    char *dcode;
-    int sdirlen;
-    int r;
-    MD5Node *node;
-
-    r = -1;
-
-    if ((sfile = strrchr(spath, '/')) != NULL)
-       ++sfile;
-    else
-       sfile = spath;
-    sdirlen = sfile - spath;
-
-    md5_cache(spath, sdirlen);
-
-    node = md5_lookup(sfile);
-
-    /*
-     * If dpath == NULL, we are force-updating the source .MD5* files
-     */
-
-    if (dpath == NULL) {
-       char *scode = doMD5File(spath, NULL);
-
-       r = 0;
-       if (node->md_Code == NULL) {
-           r = -1;
-           node->md_Code = scode;
-           MD5SCacheDirty = 1;
-       } else if (strcmp(scode, node->md_Code) != 0) {
-           r = -1;
-           free(node->md_Code);
-           node->md_Code = scode;
-           MD5SCacheDirty = 1;
-       } else {
-           free(scode);
-       }
-       return(r);
-    }
-
-    /*
-     * Otherwise the .MD5* file is used as a cache.
-     */
-
-    if (node->md_Code == NULL) {
-       node->md_Code = doMD5File(spath, NULL);
-       MD5SCacheDirty = 1;
-    }
-
-    dcode = doMD5File(dpath, NULL);
-    if (dcode) {
-       if (strcmp(node->md_Code, dcode) == 0) {
-           r = 0;
-       } else {
-           char *scode = doMD5File(spath, NULL);
-
-           if (strcmp(node->md_Code, scode) == 0) {
-                   free(scode);
-           } else {
-                   free(node->md_Code);
-                   node->md_Code = scode;
-                   MD5SCacheDirty = 1;
-                   if (strcmp(node->md_Code, dcode) == 0)
-                       r = 0;
-           }
-       }
-       free(dcode);
-    }
-    return(r);
-}
-
 /*
  * xrename() - rename with override
  *
@@ -1341,59 +1137,3 @@ xlink(const char *src, const char *dst, u_long flags)
     return(r);
 }
 
-static char *
-fextract(FILE *fi, int n, int *pc, int skip)
-{
-    int i;
-    int c;
-    int imax;
-    char *s;
-
-    i = 0;
-    c = *pc;
-    imax = (n < 0) ? 64 : n + 1;
-
-    s = malloc(imax);
-    if (s == NULL) {
-       fprintf(stderr, "out of memory\n");
-       exit(EXIT_FAILURE);
-    }
-
-    while (c != EOF) {
-       if (n == 0 || (n < 0 && (c == ' ' || c == '\n')))
-           break;
-
-       s[i++] = c;
-       if (i == imax) {
-           imax += 64;
-           s = realloc(s, imax);
-           if (s == NULL) {
-                fprintf(stderr, "out of memory\n");
-               exit(EXIT_FAILURE);
-           }
-       }
-       if (n > 0)
-           --n;
-       c = getc(fi);
-    }
-    if (c == skip && skip != EOF)
-       c = getc(fi);
-    *pc = c;
-    s[i] = 0;
-    return(s);
-}
-
-char *
-doMD5File(const char *filename, char *buf)
-{
-    if (SummaryOpt) {
-       struct stat st;
-       if (stat(filename, &st) == 0) {
-           u_int64_t size = st.st_blocks * 512;
-           if (st.st_size % 512) 
-               size += st.st_size % 512 - 512;
-           CountReadBytes += size;
-       }
-    }
-    return MD5File(filename, buf);
-}
index beec23b..c1f06ce 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * CPDUP.H
  *
- * $DragonFly: src/bin/cpdup/cpdup.h,v 1.2 2004/08/25 01:38:50 dillon Exp $
+ * $DragonFly: src/bin/cpdup/cpdup.h,v 1.3 2006/04/25 21:30:45 dillon Exp $
  */
 
 #include <sys/param.h>
 #include <assert.h>
 #include <md5.h>
 
-extern void logstd(const char *ctl, ...);
-extern void logerr(const char *ctl, ...);
-extern char *mprintf(const char *ctl, ...);
-extern void fatal(const char *ctl, ...);
+void logstd(const char *ctl, ...);
+void logerr(const char *ctl, ...);
+char *mprintf(const char *ctl, ...);
+void fatal(const char *ctl, ...);
+char *fextract(FILE *fi, int n, int *pc, int skip);
+
+int fsmid_check(int64_t fsmid, const char *dpath);
+void fsmid_flush(void);
+int md5_check(const char *spath, const char *dpath);
+void md5_flush(void);
+
+extern const char *MD5CacheFile;
+extern const char *FSMIDCacheFile;
+
+extern int SummaryOpt;
+
+extern int64_t CountSourceBytes;
+extern int64_t CountSourceItems;
+extern int64_t CountCopiedItems;
+extern int64_t CountReadBytes;
+extern int64_t CountWriteBytes;
+extern int64_t CountRemovedItems;
 
diff --git a/bin/cpdup/fsmid.c b/bin/cpdup/fsmid.c
new file mode 100644 (file)
index 0000000..8493525
--- /dev/null
@@ -0,0 +1,204 @@
+/*-
+ * FSMID.C
+ *
+ * (c) Copyright 1997-1999,2006 by Matthew Dillon.  Permission to
+ *     use and distribute based on the FreeBSD copyright.
+ *
+ * $DragonFly: src/bin/cpdup/fsmid.c,v 1.1 2006/04/25 21:30:45 dillon Exp $
+ */
+
+#include "cpdup.h"
+
+typedef struct FSMIDNode {
+    struct FSMIDNode *fid_Next;
+    char *fid_Name;
+    int64_t fid_Code;
+    int fid_Accessed;
+} FSMIDNode;
+
+static FSMIDNode *fsmid_lookup(const char *sfile);
+static void fsmid_cache(const char *dpath, int ddirlen);
+
+static char *FSMIDDCache;      /* cache source directory name */
+static FSMIDNode *FSMIDBase;
+static int FSMIDDCacheDirLen;
+static int FSMIDDCacheDirty;
+
+void 
+fsmid_flush(void)
+{
+    if (FSMIDDCacheDirty && FSMIDDCache) {
+       FILE *fo;
+
+       if ((fo = fopen(FSMIDDCache, "w")) != NULL) {
+           FSMIDNode *node;
+
+           for (node = FSMIDBase; node; node = node->fid_Next) {
+               if (node->fid_Accessed && node->fid_Code) {
+                   fprintf(fo, "%016llx %d %s\n", 
+                       node->fid_Code, 
+                       strlen(node->fid_Name),
+                       node->fid_Name
+                   );
+               }
+           }
+           fclose(fo);
+       }
+    }
+
+    FSMIDDCacheDirty = 0;
+
+    if (FSMIDDCache) {
+       FSMIDNode *node;
+
+       while ((node = FSMIDBase) != NULL) {
+           FSMIDBase = node->fid_Next;
+
+           if (node->fid_Name)
+               free(node->fid_Name);
+           free(node);
+       }
+       free(FSMIDDCache);
+       FSMIDDCache = NULL;
+    }
+}
+
+static void
+fsmid_cache(const char *dpath, int ddirlen)
+{
+    FILE *fi;
+
+    /*
+     * Already cached
+     */
+
+    if (
+       FSMIDDCache &&
+       ddirlen == FSMIDDCacheDirLen &&
+       strncmp(dpath, FSMIDDCache, ddirlen) == 0
+    ) {
+       return;
+    }
+
+    /*
+     * Different cache, flush old cache
+     */
+
+    if (FSMIDDCache != NULL)
+       fsmid_flush();
+
+    /*
+     * Create new cache
+     */
+
+    FSMIDDCacheDirLen = ddirlen;
+    FSMIDDCache = mprintf("%*.*s%s", ddirlen, ddirlen, dpath, FSMIDCacheFile);
+
+    if ((fi = fopen(FSMIDDCache, "r")) != NULL) {
+       FSMIDNode **pnode = &FSMIDBase;
+       int c;
+
+       c = fgetc(fi);
+       while (c != EOF) {
+           FSMIDNode *node = *pnode = malloc(sizeof(FSMIDNode));
+           char *s;
+           int nlen;
+
+           nlen = 0;
+
+           if (pnode == NULL || node == NULL) {
+               fprintf(stderr, "out of memory\n");
+               exit(EXIT_FAILURE);
+           }
+
+           bzero(node, sizeof(FSMIDNode));
+           node->fid_Code = strtoull(fextract(fi, -1, &c, ' '), NULL, 16);
+           node->fid_Accessed = 1;
+           if ((s = fextract(fi, -1, &c, ' ')) != NULL) {
+               nlen = strtol(s, NULL, 0);
+               free(s);
+           }
+           /*
+            * extracting fid_Name - name may contain embedded control 
+            * characters.
+            */
+           CountReadBytes += nlen+1;
+           node->fid_Name = fextract(fi, nlen, &c, EOF);
+           if (c != '\n') {
+               fprintf(stderr, "Error parsing FSMID Cache: %s (%c)\n", FSMIDDCache, c);
+               while (c != EOF && c != '\n')
+                   c = fgetc(fi);
+           }
+           if (c != EOF)
+               c = fgetc(fi);
+           pnode = &node->fid_Next;
+       }
+       fclose(fi);
+    }
+}
+
+/*
+ * fsmid_lookup:       lookup/create fsmid entry
+ */
+
+static FSMIDNode *
+fsmid_lookup(const char *sfile)
+{
+    FSMIDNode **pnode;
+    FSMIDNode *node;
+
+    for (pnode = &FSMIDBase; (node = *pnode) != NULL; pnode = &node->fid_Next) {
+       if (strcmp(sfile, node->fid_Name) == 0) {
+           break;
+       }
+    }
+    if (node == NULL) {
+       if ((node = *pnode = malloc(sizeof(FSMIDNode))) == NULL) {
+               fprintf(stderr,"out of memory\n");
+               exit(EXIT_FAILURE);
+       }
+       bzero(node, sizeof(FSMIDNode));
+       node->fid_Name = strdup(sfile);
+       FSMIDDCacheDirty = 1;
+    }
+    node->fid_Accessed = 1;
+    return(node);
+}
+
+/*
+ * fsmid_check:  check FSMID against file
+ *
+ *     Return -1 if check failed
+ *     Return 0  if check succeeded
+ *
+ * dpath can be NULL, in which case we are force-updating
+ * the source FSMID.
+ */
+int
+fsmid_check(int64_t fsmid, const char *dpath)
+{
+    const char *dfile;
+    int ddirlen;
+    int r;
+    FSMIDNode *node;
+
+    r = -1;
+
+    if ((dfile = strrchr(dpath, '/')) != NULL)
+       ++dfile;
+    else
+       dfile = dpath;
+    ddirlen = dfile - dpath;
+
+    fsmid_cache(dpath, ddirlen);
+
+    node = fsmid_lookup(dfile);
+
+    if (node->fid_Code != fsmid) {
+       node->fid_Code = fsmid;
+       FSMIDDCacheDirty = 1;
+       return(1);
+    }
+    return(0);
+}
+
diff --git a/bin/cpdup/md5.c b/bin/cpdup/md5.c
new file mode 100644 (file)
index 0000000..607b7a3
--- /dev/null
@@ -0,0 +1,271 @@
+/*-
+ * MD5.C
+ *
+ * (c) Copyright 1997-1999 by Matthew Dillon and Dima Ruban.  Permission to
+ *     use and distribute based on the FreeBSD copyright.  Supplied as-is,
+ *     USE WITH EXTREME CAUTION.
+ *
+ * $DragonFly: src/bin/cpdup/md5.c,v 1.1 2006/04/25 21:30:45 dillon Exp $
+ */
+
+#include "cpdup.h"
+
+typedef struct MD5Node {
+    struct MD5Node *md_Next;
+    char *md_Name;
+    char *md_Code;
+    int md_Accessed;
+} MD5Node;
+
+static MD5Node *md5_lookup(const char *sfile);
+static void md5_cache(const char *spath, int sdirlen);
+static char *doMD5File(const char *filename, char *buf);
+
+static char *MD5SCache;                /* cache source directory name */
+static MD5Node *MD5Base;
+static int MD5SCacheDirLen;
+static int MD5SCacheDirty;
+
+void 
+md5_flush(void)
+{
+    if (MD5SCacheDirty && MD5SCache) {
+       FILE *fo;
+
+       if ((fo = fopen(MD5SCache, "w")) != NULL) {
+           MD5Node *node;
+
+           for (node = MD5Base; node; node = node->md_Next) {
+               if (node->md_Accessed && node->md_Code) {
+                   fprintf(fo, "%s %d %s\n", 
+                       node->md_Code, 
+                       strlen(node->md_Name),
+                       node->md_Name
+                   );
+               }
+           }
+           fclose(fo);
+       }
+    }
+
+    MD5SCacheDirty = 0;
+
+    if (MD5SCache) {
+       MD5Node *node;
+
+       while ((node = MD5Base) != NULL) {
+           MD5Base = node->md_Next;
+
+           if (node->md_Code)
+               free(node->md_Code);
+           if (node->md_Name)
+               free(node->md_Name);
+           free(node);
+       }
+       free(MD5SCache);
+       MD5SCache = NULL;
+    }
+}
+
+static void
+md5_cache(const char *spath, int sdirlen)
+{
+    FILE *fi;
+
+    /*
+     * Already cached
+     */
+
+    if (
+       MD5SCache &&
+       sdirlen == MD5SCacheDirLen &&
+       strncmp(spath, MD5SCache, sdirlen) == 0
+    ) {
+       return;
+    }
+
+    /*
+     * Different cache, flush old cache
+     */
+
+    if (MD5SCache != NULL)
+       md5_flush();
+
+    /*
+     * Create new cache
+     */
+
+    MD5SCacheDirLen = sdirlen;
+    MD5SCache = mprintf("%*.*s%s", sdirlen, sdirlen, spath, MD5CacheFile);
+
+    if ((fi = fopen(MD5SCache, "r")) != NULL) {
+       MD5Node **pnode = &MD5Base;
+       int c;
+
+       c = fgetc(fi);
+       while (c != EOF) {
+           MD5Node *node = *pnode = malloc(sizeof(MD5Node));
+           char *s;
+           int nlen;
+
+           nlen = 0;
+
+           if (pnode == NULL || node == NULL) {
+               fprintf(stderr, "out of memory\n");
+               exit(EXIT_FAILURE);
+           }
+
+           bzero(node, sizeof(MD5Node));
+           node->md_Code = fextract(fi, -1, &c, ' ');
+           node->md_Accessed = 1;
+           if ((s = fextract(fi, -1, &c, ' ')) != NULL) {
+               nlen = strtol(s, NULL, 0);
+               free(s);
+           }
+           /*
+            * extracting md_Name - name may contain embedded control 
+            * characters.
+            */
+           CountReadBytes += nlen+1;
+           node->md_Name = fextract(fi, nlen, &c, EOF);
+           if (c != '\n') {
+               fprintf(stderr, "Error parsing MD5 Cache: %s (%c)\n", MD5SCache, c);
+               while (c != EOF && c != '\n')
+                   c = fgetc(fi);
+           }
+           if (c != EOF)
+               c = fgetc(fi);
+           pnode = &node->md_Next;
+       }
+       fclose(fi);
+    }
+}
+
+/*
+ * md5_lookup: lookup/create md5 entry
+ */
+
+static MD5Node *
+md5_lookup(const char *sfile)
+{
+    MD5Node **pnode;
+    MD5Node *node;
+
+    for (pnode = &MD5Base; (node = *pnode) != NULL; pnode = &node->md_Next) {
+       if (strcmp(sfile, node->md_Name) == 0) {
+           break;
+       }
+    }
+    if (node == NULL) {
+
+       if ((node = *pnode = malloc(sizeof(MD5Node))) == NULL) {
+               fprintf(stderr,"out of memory\n");
+               exit(EXIT_FAILURE);
+       }
+
+       bzero(node, sizeof(MD5Node));
+       node->md_Name = strdup(sfile);
+    }
+    node->md_Accessed = 1;
+    return(node);
+}
+
+/*
+ * md5_check:  check MD5 against file
+ *
+ *     Return -1 if check failed
+ *     Return 0  if check succeeded
+ *
+ * dpath can be NULL, in which case we are force-updating
+ * the source MD5.
+ */
+int
+md5_check(const char *spath, const char *dpath)
+{
+    const char *sfile;
+    char *dcode;
+    int sdirlen;
+    int r;
+    MD5Node *node;
+
+    r = -1;
+
+    if ((sfile = strrchr(spath, '/')) != NULL)
+       ++sfile;
+    else
+       sfile = spath;
+    sdirlen = sfile - spath;
+
+    md5_cache(spath, sdirlen);
+
+    node = md5_lookup(sfile);
+
+    /*
+     * If dpath == NULL, we are force-updating the source .MD5* files
+     */
+
+    if (dpath == NULL) {
+       char *scode = doMD5File(spath, NULL);
+
+       r = 0;
+       if (node->md_Code == NULL) {
+           r = -1;
+           node->md_Code = scode;
+           MD5SCacheDirty = 1;
+       } else if (strcmp(scode, node->md_Code) != 0) {
+           r = -1;
+           free(node->md_Code);
+           node->md_Code = scode;
+           MD5SCacheDirty = 1;
+       } else {
+           free(scode);
+       }
+       return(r);
+    }
+
+    /*
+     * Otherwise the .MD5* file is used as a cache.
+     */
+
+    if (node->md_Code == NULL) {
+       node->md_Code = doMD5File(spath, NULL);
+       MD5SCacheDirty = 1;
+    }
+
+    dcode = doMD5File(dpath, NULL);
+    if (dcode) {
+       if (strcmp(node->md_Code, dcode) == 0) {
+           r = 0;
+       } else {
+           char *scode = doMD5File(spath, NULL);
+
+           if (strcmp(node->md_Code, scode) == 0) {
+                   free(scode);
+           } else {
+                   free(node->md_Code);
+                   node->md_Code = scode;
+                   MD5SCacheDirty = 1;
+                   if (strcmp(node->md_Code, dcode) == 0)
+                       r = 0;
+           }
+       }
+       free(dcode);
+    }
+    return(r);
+}
+
+char *
+doMD5File(const char *filename, char *buf)
+{
+    if (SummaryOpt) {
+       struct stat st;
+       if (stat(filename, &st) == 0) {
+           u_int64_t size = st.st_blocks * 512;
+           if (st.st_size % 512) 
+               size += st.st_size % 512 - 512;
+           CountReadBytes += size;
+       }
+    }
+    return MD5File(filename, buf);
+}
+
index 3dd57e9..246d1bb 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * MISC.C
  *
- * $DragonFly: src/bin/cpdup/misc.c,v 1.5 2004/08/25 01:38:50 dillon Exp $
+ * $DragonFly: src/bin/cpdup/misc.c,v 1.6 2006/04/25 21:30:45 dillon Exp $
  */
 
 #include "cpdup.h"
@@ -42,6 +42,48 @@ mprintf(const char *ctl, ...)
     return(ptr);
 }
 
+char *
+fextract(FILE *fi, int n, int *pc, int skip)
+{
+    int i;
+    int c;
+    int imax;
+    char *s;
+
+    i = 0;
+    c = *pc;
+    imax = (n < 0) ? 64 : n + 1;
+
+    s = malloc(imax);
+    if (s == NULL) {
+       fprintf(stderr, "out of memory\n");
+       exit(EXIT_FAILURE);
+    }
+
+    while (c != EOF) {
+       if (n == 0 || (n < 0 && (c == ' ' || c == '\n')))
+           break;
+
+       s[i++] = c;
+       if (i == imax) {
+           imax += 64;
+           s = realloc(s, imax);
+           if (s == NULL) {
+                fprintf(stderr, "out of memory\n");
+               exit(EXIT_FAILURE);
+           }
+       }
+       if (n > 0)
+           --n;
+       c = getc(fi);
+    }
+    if (c == skip && skip != EOF)
+       c = getc(fi);
+    *pc = c;
+    s[i] = 0;
+    return(s);
+}
+
 void
 fatal(const char *ctl, ...)
 {