[Midnightbsd-cvs] src: bin/cpdup: Sync with DragonFly.

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Thu Apr 10 19:45:55 EDT 2008


Log Message:
-----------
Sync with DragonFly.

Matt added several features to cpdup including parallel transaction support for remote source or targets.

By default, this now compiles with pthreads support.

-l was added to force stdout and stderr to be line-buffered.

Also, we had to modify the casts for pointer types.  It is nice to have amd64 build things since pointers aren't ints :)

Modified Files:
--------------
    src/bin/cpdup:
        BACKUPS (r1.1 -> r1.2)
        Makefile (r1.1 -> r1.2)
        PORTING (r1.1 -> r1.2)
        cpdup.1 (r1.1 -> r1.2)
        cpdup.c (r1.1 -> r1.2)
        cpdup.h (r1.1 -> r1.2)
        fsmid.c (r1.1 -> r1.2)
        hclink.c (r1.1 -> r1.2)
        hclink.h (r1.1 -> r1.2)
        hcproto.c (r1.1 -> r1.2)
        hcproto.h (r1.1 -> r1.2)
        md5.c (r1.1 -> r1.2)
        misc.c (r1.1 -> r1.2)
    src/bin/cpdup/scripts:
        crontab (r1.1 -> r1.2)
        do_cleanup (r1.1 -> r1.2)
        do_mirror (r1.1 -> r1.2)
        do_mirror_host (r1.1 -> r1.2)
        do_remote (r1.1 -> r1.2)
        do_remote_host (r1.1 -> r1.2)
        params (r1.1 -> r1.2)

-------------- next part --------------
Index: hcproto.c
===================================================================
RCS file: /home/cvs/src/bin/cpdup/hcproto.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L bin/cpdup/hcproto.c -L bin/cpdup/hcproto.c -u -r1.1 -r1.2
--- bin/cpdup/hcproto.c
+++ bin/cpdup/hcproto.c
@@ -3,51 +3,41 @@
  *
  * This module implements a simple remote control protocol
  *
- * $MidnightBSD$
- * $DragonFly: src/bin/cpdup/hcproto.c,v 1.1 2006/08/13 20:51:40 dillon Exp $
+ * $DragonFly: src/bin/cpdup/hcproto.c,v 1.2 2008/04/10 22:09:08 dillon Exp $
  */
 
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <errno.h>
-
+#include "cpdup.h"
 #include "hclink.h"
 #include "hcproto.h"
 
 static int hc_decode_stat(struct stat *, struct HCHead *);
-static int rc_encode_stat(struct HostConf *, struct stat *);
+static int rc_encode_stat(hctransaction_t trans, struct stat *);
 
-static int rc_hello(struct HostConf *, struct HCHead *);
-static int rc_stat(struct HostConf *, struct HCHead *);
-static int rc_lstat(struct HostConf *, struct HCHead *);
-static int rc_opendir(struct HostConf *, struct HCHead *);
-static int rc_readdir(struct HostConf *, struct HCHead *);
-static int rc_closedir(struct HostConf *, struct HCHead *);
-static int rc_open(struct HostConf *, struct HCHead *);
-static int rc_close(struct HostConf *, struct HCHead *);
-static int rc_read(struct HostConf *, struct HCHead *);
-static int rc_write(struct HostConf *, struct HCHead *);
-static int rc_remove(struct HostConf *, struct HCHead *);
-static int rc_mkdir(struct HostConf *, struct HCHead *);
-static int rc_rmdir(struct HostConf *, struct HCHead *);
-static int rc_chown(struct HostConf *, struct HCHead *);
-static int rc_lchown(struct HostConf *, struct HCHead *);
-static int rc_chmod(struct HostConf *, struct HCHead *);
-static int rc_link(struct HostConf *, struct HCHead *);
+static int rc_hello(hctransaction_t trans, struct HCHead *);
+static int rc_stat(hctransaction_t trans, struct HCHead *);
+static int rc_lstat(hctransaction_t trans, struct HCHead *);
+static int rc_opendir(hctransaction_t trans, struct HCHead *);
+static int rc_readdir(hctransaction_t trans, struct HCHead *);
+static int rc_closedir(hctransaction_t trans, struct HCHead *);
+static int rc_open(hctransaction_t trans, struct HCHead *);
+static int rc_close(hctransaction_t trans, struct HCHead *);
+static int rc_read(hctransaction_t trans, struct HCHead *);
+static int rc_write(hctransaction_t trans, struct HCHead *);
+static int rc_remove(hctransaction_t trans, struct HCHead *);
+static int rc_mkdir(hctransaction_t trans, struct HCHead *);
+static int rc_rmdir(hctransaction_t trans, struct HCHead *);
+static int rc_chown(hctransaction_t trans, struct HCHead *);
+static int rc_lchown(hctransaction_t trans, struct HCHead *);
+static int rc_chmod(hctransaction_t trans, struct HCHead *);
+static int rc_link(hctransaction_t trans, struct HCHead *);
 #ifdef _ST_FLAGS_PRESENT_
-static int rc_chflags(struct HostConf *, struct HCHead *);
+static int rc_chflags(hctransaction_t trans, struct HCHead *);
 #endif
-static int rc_readlink(struct HostConf *, struct HCHead *);
-static int rc_umask(struct HostConf *, struct HCHead *);
-static int rc_symlink(struct HostConf *, struct HCHead *);
-static int rc_rename(struct HostConf *, struct HCHead *);
-static int rc_utimes(struct HostConf *, struct HCHead *);
+static int rc_readlink(hctransaction_t trans, struct HCHead *);
+static int rc_umask(hctransaction_t trans, struct HCHead *);
+static int rc_symlink(hctransaction_t trans, struct HCHead *);
+static int rc_rename(hctransaction_t trans, struct HCHead *);
+static int rc_utimes(hctransaction_t trans, struct HCHead *);
 
 struct HCDesc HCDispatchTable[] = {
     { HC_HELLO,		rc_hello },
@@ -101,6 +91,7 @@
 {
     struct HCHead *head;
     struct HCLeaf *item;
+    hctransaction_t trans;
     char hostbuf[256];
     int error;
 
@@ -110,9 +101,10 @@
     if (hostbuf[0] == 0)
 	hostbuf[0] = '?';
 
-    hcc_start_command(hc, HC_HELLO);
-    hcc_leaf_string(hc, LC_HELLOSTR, hostbuf);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_HELLO);
+    hcc_leaf_string(trans, LC_HELLOSTR, hostbuf);
+    hcc_leaf_int32(trans, LC_VERSION, 1);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
 
     if (head->error) {
@@ -127,6 +119,10 @@
 	case LC_HELLOSTR:
 	    fprintf(stderr, "Handshaked with %s\n", HCC_STRING(item));
 	    error = 0;
+	    break;
+	case LC_VERSION:
+	    hc->version = HCC_INT32(item);
+	    break;
 	}
     }
     if (error < 0)
@@ -135,7 +131,7 @@
 }
 
 static int
-rc_hello(struct HostConf *hc, struct HCHead *head __unused)
+rc_hello(hctransaction_t trans, struct HCHead *head __unused)
 {
     char hostbuf[256];
 
@@ -145,7 +141,8 @@
     if (hostbuf[0] == 0)
 	hostbuf[0] = '?';
 
-    hcc_leaf_string(hc, LC_HELLOSTR, hostbuf);
+    hcc_leaf_string(trans, LC_HELLOSTR, hostbuf);
+    hcc_leaf_int32(trans, LC_VERSION, 1);
     return(0);
 }
 
@@ -156,13 +153,14 @@
 hc_stat(struct HostConf *hc, const char *path, struct stat *st)
 {
     struct HCHead *head;
+    hctransaction_t trans;
 
     if (hc == NULL || hc->host == NULL)
 	return(stat(path, st));
 
-    hcc_start_command(hc, HC_STAT);
-    hcc_leaf_string(hc, LC_PATH1, path);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_STAT);
+    hcc_leaf_string(trans, LC_PATH1, path);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -173,13 +171,14 @@
 hc_lstat(struct HostConf *hc, const char *path, struct stat *st)
 {
     struct HCHead *head;
+    hctransaction_t trans;
 
     if (hc == NULL || hc->host == NULL)
 	return(lstat(path, st));
 
-    hcc_start_command(hc, HC_LSTAT);
-    hcc_leaf_string(hc, LC_PATH1, path);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_LSTAT);
+    hcc_leaf_string(trans, LC_PATH1, path);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -249,7 +248,7 @@
 }
 
 static int
-rc_stat(struct HostConf *hc, struct HCHead *head)
+rc_stat(hctransaction_t trans, struct HCHead *head)
 {
     struct HCLeaf *item;
     struct stat st;
@@ -266,11 +265,11 @@
 	return(-2);
     if (stat(path, &st) < 0)
 	return(-1);
-    return (rc_encode_stat(hc, &st));
+    return (rc_encode_stat(trans, &st));
 }
 
 static int
-rc_lstat(struct HostConf *hc, struct HCHead *head)
+rc_lstat(hctransaction_t trans, struct HCHead *head)
 {
     struct HCLeaf *item;
     struct stat st;
@@ -287,30 +286,30 @@
 	return(-2);
     if (lstat(path, &st) < 0)
 	return(-1);
-    return (rc_encode_stat(hc, &st));
+    return (rc_encode_stat(trans, &st));
 }
 
 static int
-rc_encode_stat(struct HostConf *hc, struct stat *st)
+rc_encode_stat(hctransaction_t trans, struct stat *st)
 {
-    hcc_leaf_int32(hc, LC_DEV, st->st_dev);
-    hcc_leaf_int64(hc, LC_INO, st->st_ino);
-    hcc_leaf_int32(hc, LC_MODE, st->st_mode);
-    hcc_leaf_int32(hc, LC_NLINK, st->st_nlink);
-    hcc_leaf_int32(hc, LC_UID, st->st_uid);
-    hcc_leaf_int32(hc, LC_GID, st->st_gid);
-    hcc_leaf_int32(hc, LC_RDEV, st->st_rdev);
-    hcc_leaf_int64(hc, LC_ATIME, st->st_atime);
-    hcc_leaf_int64(hc, LC_MTIME, st->st_mtime);
-    hcc_leaf_int64(hc, LC_CTIME, st->st_ctime);
-    hcc_leaf_int64(hc, LC_FILESIZE, st->st_size);
-    hcc_leaf_int64(hc, LC_FILEBLKS, st->st_blocks);
-    hcc_leaf_int32(hc, LC_BLKSIZE, st->st_blksize);
+    hcc_leaf_int32(trans, LC_DEV, st->st_dev);
+    hcc_leaf_int64(trans, LC_INO, st->st_ino);
+    hcc_leaf_int32(trans, LC_MODE, st->st_mode);
+    hcc_leaf_int32(trans, LC_NLINK, st->st_nlink);
+    hcc_leaf_int32(trans, LC_UID, st->st_uid);
+    hcc_leaf_int32(trans, LC_GID, st->st_gid);
+    hcc_leaf_int32(trans, LC_RDEV, st->st_rdev);
+    hcc_leaf_int64(trans, LC_ATIME, st->st_atime);
+    hcc_leaf_int64(trans, LC_MTIME, st->st_mtime);
+    hcc_leaf_int64(trans, LC_CTIME, st->st_ctime);
+    hcc_leaf_int64(trans, LC_FILESIZE, st->st_size);
+    hcc_leaf_int64(trans, LC_FILEBLKS, st->st_blocks);
+    hcc_leaf_int32(trans, LC_BLKSIZE, st->st_blksize);
 #ifdef _ST_FSMID_PRESENT_
-    hcc_leaf_int64(hc, LC_FSMID, st->st_fsmid);
+    hcc_leaf_int64(trans, LC_FSMID, st->st_fsmid);
 #endif
 #ifdef _ST_FLAGS_PRESENT_
-    hcc_leaf_int64(hc, LC_FILEFLAGS, st->st_flags);
+    hcc_leaf_int64(trans, LC_FILEFLAGS, st->st_flags);
 #endif
     return(0);
 }
@@ -321,6 +320,7 @@
 DIR *
 hc_opendir(struct HostConf *hc, const char *path)
 {
+    hctransaction_t trans;
     struct HCHead *head;
     struct HCLeaf *item;
     struct dirent *den;
@@ -329,9 +329,9 @@
     if (hc == NULL || hc->host == NULL)
 	return(opendir(path));
 
-    hcc_start_command(hc, HC_OPENDIR);
-    hcc_leaf_string(hc, LC_PATH1, path);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_OPENDIR);
+    hcc_leaf_string(trans, LC_PATH1, path);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(NULL);
     if (head->error)
 	return(NULL);
@@ -354,7 +354,7 @@
 }
 
 static int
-rc_opendir(struct HostConf *hc, struct HCHead *head)
+rc_opendir(hctransaction_t trans, struct HCHead *head)
 {
     struct HCLeaf *item;
     const char *path = NULL;
@@ -373,8 +373,8 @@
     if ((dir = opendir(path)) == NULL) {
 	head->error = errno;
     } else {
-	desc = hcc_alloc_descriptor(hc, dir, HC_DESC_DIR);
-	hcc_leaf_int32(hc, LC_DESCRIPTOR, desc);
+	desc = hcc_alloc_descriptor(trans->hc, dir, HC_DESC_DIR);
+	hcc_leaf_int32(trans, LC_DESCRIPTOR, desc);
     }
     return(0);
 }
@@ -385,6 +385,7 @@
 struct dirent *
 hc_readdir(struct HostConf *hc, DIR *dir)
 {
+    hctransaction_t trans;
     struct HCHead *head;
     struct HCLeaf *item;
     struct dirent *den;
@@ -392,9 +393,9 @@
     if (hc == NULL || hc->host == NULL)
 	return(readdir(dir));
 
-    hcc_start_command(hc, HC_READDIR);
-    hcc_leaf_int32(hc, LC_DESCRIPTOR, (long)dir);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_READDIR);
+    hcc_leaf_int32(trans, LC_DESCRIPTOR, (long)dir);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(NULL);
     if (head->error)
 	return(NULL);	/* XXX errno */
@@ -426,7 +427,7 @@
 }
 
 static int
-rc_readdir(struct HostConf *hc, struct HCHead *head)
+rc_readdir(hctransaction_t trans, struct HCHead *head)
 {
     struct HCLeaf *item;
     struct dirent *den;
@@ -435,16 +436,16 @@
     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
 	switch(item->leafid) {
 	case LC_DESCRIPTOR:
-	    dir = hcc_get_descriptor(hc, HCC_INT32(item), HC_DESC_DIR);
+	    dir = hcc_get_descriptor(trans->hc, HCC_INT32(item), HC_DESC_DIR);
 	    break;
 	}
     }
     if (dir == NULL)
 	return(-2);
     if ((den = readdir(dir)) != NULL) {
-	hcc_leaf_string(hc, LC_PATH1, den->d_name);
-	hcc_leaf_int64(hc, LC_INO, den->d_fileno);
-	hcc_leaf_int32(hc, LC_TYPE, den->d_type);
+	hcc_leaf_string(trans, LC_PATH1, den->d_name);
+	hcc_leaf_int64(trans, LC_INO, den->d_fileno);
+	hcc_leaf_int32(trans, LC_TYPE, den->d_type);
     }
     return(0);
 }
@@ -457,6 +458,7 @@
 int
 hc_closedir(struct HostConf *hc, DIR *dir)
 {
+    hctransaction_t trans;
     struct HCHead *head;
     struct dirent *den;
 
@@ -467,9 +469,9 @@
 	free(den);
 	hcc_set_descriptor(hc, (long)dir, NULL, HC_DESC_DIR);
 
-	hcc_start_command(hc, HC_CLOSEDIR);
-	hcc_leaf_int32(hc, LC_DESCRIPTOR, (long)dir);
-	if ((head = hcc_finish_command(hc)) == NULL)
+	trans = hcc_start_command(hc, HC_CLOSEDIR);
+	hcc_leaf_int32(trans, LC_DESCRIPTOR, (long)dir);
+	if ((head = hcc_finish_command(trans)) == NULL)
 	    return(-1);
 	if (head->error)
 	    return(-1);		/* XXX errno */
@@ -481,7 +483,7 @@
 }
 
 static int
-rc_closedir(struct HostConf *hc, struct HCHead *head)
+rc_closedir(hctransaction_t trans, struct HCHead *head)
 {
     struct HCLeaf *item;
     DIR *dir = NULL;
@@ -489,7 +491,7 @@
     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
 	switch(item->leafid) {
 	case LC_DESCRIPTOR:
-	    dir = hcc_get_descriptor(hc, HCC_INT32(item), HC_DESC_DIR);
+	    dir = hcc_get_descriptor(trans->hc, HCC_INT32(item), HC_DESC_DIR);
 	    break;
 	}
     }
@@ -504,6 +506,7 @@
 int
 hc_open(struct HostConf *hc, const char *path, int flags, mode_t mode)
 {
+    hctransaction_t trans;
     struct HCHead *head;
     struct HCLeaf *item;
     int *fdp;
@@ -525,12 +528,12 @@
     if (flags & O_TRUNC)
 	nflags |= XO_TRUNC;
 
-    hcc_start_command(hc, HC_OPEN);
-    hcc_leaf_string(hc, LC_PATH1, path);
-    hcc_leaf_int32(hc, LC_OFLAGS, nflags);
-    hcc_leaf_int32(hc, LC_MODE, mode);
+    trans = hcc_start_command(hc, HC_OPEN);
+    hcc_leaf_string(trans, LC_PATH1, path);
+    hcc_leaf_int32(trans, LC_OFLAGS, nflags);
+    hcc_leaf_int32(trans, LC_MODE, mode);
 
-    if ((head = hcc_finish_command(hc)) == NULL)
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -553,7 +556,7 @@
 }
 
 static int
-rc_open(struct HostConf *hc, struct HCHead *head)
+rc_open(hctransaction_t trans, struct HCHead *head)
 {
     struct HCLeaf *item;
     const char *path = NULL;
@@ -597,8 +600,8 @@
     }
     fdp = malloc(sizeof(int));
     *fdp = fd;
-    desc = hcc_alloc_descriptor(hc, fdp, HC_DESC_FD);
-    hcc_leaf_int32(hc, LC_DESCRIPTOR, desc);
+    desc = hcc_alloc_descriptor(trans->hc, fdp, HC_DESC_FD);
+    hcc_leaf_int32(trans, LC_DESCRIPTOR, desc);
     return(0);
 }
 
@@ -608,6 +611,7 @@
 int
 hc_close(struct HostConf *hc, int fd)
 {
+    hctransaction_t trans;
     struct HCHead *head;
     int *fdp;
 
@@ -619,9 +623,9 @@
 	free(fdp);
 	hcc_set_descriptor(hc, fd, NULL, HC_DESC_FD);
 
-	hcc_start_command(hc, HC_CLOSE);
-	hcc_leaf_int32(hc, LC_DESCRIPTOR, fd);
-	if ((head = hcc_finish_command(hc)) == NULL)
+	trans = hcc_start_command(hc, HC_CLOSE);
+	hcc_leaf_int32(trans, LC_DESCRIPTOR, fd);
+	if ((head = hcc_finish_command(trans)) == NULL)
 	    return(-1);
 	if (head->error)
 	    return(-1);
@@ -632,7 +636,7 @@
 }
 
 static int
-rc_close(struct HostConf *hc, struct HCHead *head)
+rc_close(hctransaction_t trans, struct HCHead *head)
 {
     struct HCLeaf *item;
     int *fdp = NULL;
@@ -648,11 +652,11 @@
     }
     if (desc < 0)
 	return(-2);
-    if ((fdp = hcc_get_descriptor(hc, desc, HC_DESC_FD)) == NULL)
+    if ((fdp = hcc_get_descriptor(trans->hc, desc, HC_DESC_FD)) == NULL)
 	return(-2);
     fd = *fdp;
     free(fdp);
-    hcc_set_descriptor(hc, desc, NULL, HC_DESC_FD);
+    hcc_set_descriptor(trans->hc, desc, NULL, HC_DESC_FD);
     return(close(fd));
 }
 
@@ -662,6 +666,7 @@
 ssize_t
 hc_read(struct HostConf *hc, int fd, void *buf, size_t bytes)
 {
+    hctransaction_t trans;
     struct HCHead *head;
     struct HCLeaf *item;
     int *fdp;
@@ -677,10 +682,10 @@
 	    int n = (bytes > 32768) ? 32768 : bytes;
 	    int x = 0;
 
-	    hcc_start_command(hc, HC_READ);
-	    hcc_leaf_int32(hc, LC_DESCRIPTOR, fd);
-	    hcc_leaf_int32(hc, LC_BYTES, n);
-	    if ((head = hcc_finish_command(hc)) == NULL)
+	    trans = hcc_start_command(hc, HC_READ);
+	    hcc_leaf_int32(trans, LC_DESCRIPTOR, fd);
+	    hcc_leaf_int32(trans, LC_BYTES, n);
+	    if ((head = hcc_finish_command(trans)) == NULL)
 		return(-1);
 	    if (head->error)
 		return(-1);
@@ -707,7 +712,7 @@
 }
 
 static int
-rc_read(struct HostConf *hc, struct HCHead *head)
+rc_read(hctransaction_t trans, struct HCHead *head)
 {
     struct HCLeaf *item;
     int *fdp = NULL;
@@ -718,7 +723,7 @@
     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
 	switch(item->leafid) {
 	case LC_DESCRIPTOR:
-	    fdp = hcc_get_descriptor(hc, HCC_INT32(item), HC_DESC_FD);
+	    fdp = hcc_get_descriptor(trans->hc, HCC_INT32(item), HC_DESC_FD);
 	    break;
 	case LC_BYTES:
 	    bytes = HCC_INT32(item);
@@ -734,7 +739,7 @@
 	head->error = errno;
 	return(0);
     }
-    hcc_leaf_data(hc, LC_DATA, buf, n);
+    hcc_leaf_data(trans, LC_DATA, buf, n);
     return(0);
 }
 
@@ -744,6 +749,7 @@
 ssize_t
 hc_write(struct HostConf *hc, int fd, const void *buf, size_t bytes)
 {
+    hctransaction_t trans;
     struct HCHead *head;
     struct HCLeaf *item;
     int *fdp;
@@ -759,10 +765,10 @@
 	    int n = (bytes > 32768) ? 32768 : bytes;
 	    int x = 0;
 
-	    hcc_start_command(hc, HC_WRITE);
-	    hcc_leaf_int32(hc, LC_DESCRIPTOR, fd);
-	    hcc_leaf_data(hc, LC_DATA, buf, n);
-	    if ((head = hcc_finish_command(hc)) == NULL)
+	    trans = hcc_start_command(hc, HC_WRITE);
+	    hcc_leaf_int32(trans, LC_DESCRIPTOR, fd);
+	    hcc_leaf_data(trans, LC_DATA, buf, n);
+	    if ((head = hcc_finish_command(trans)) == NULL)
 		return(-1);
 	    if (head->error)
 		return(-1);
@@ -788,7 +794,7 @@
 }
 
 static int
-rc_write(struct HostConf *hc, struct HCHead *head)
+rc_write(hctransaction_t trans, struct HCHead *head)
 {
     struct HCLeaf *item;
     int *fdp = NULL;
@@ -798,7 +804,7 @@
     for (item = hcc_firstitem(head); item; item = hcc_nextitem(head, item)) {
 	switch(item->leafid) {
 	case LC_DESCRIPTOR:
-	    fdp = hcc_get_descriptor(hc, HCC_INT32(item), HC_DESC_FD);
+	    fdp = hcc_get_descriptor(trans->hc, HCC_INT32(item), HC_DESC_FD);
 	    break;
 	case LC_DATA:
 	    buf = HCC_BINARYDATA(item);
@@ -814,7 +820,7 @@
     if (n < 0) {
 	head->error = errno;
     } else {
-	hcc_leaf_int32(hc, LC_BYTES, n);
+	hcc_leaf_int32(trans, LC_BYTES, n);
     }
     return(0);
 }
@@ -825,14 +831,15 @@
 int
 hc_remove(struct HostConf *hc, const char *path)
 {
+    hctransaction_t trans;
     struct HCHead *head;
 
     if (hc == NULL || hc->host == NULL)
 	return(remove(path));
 
-    hcc_start_command(hc, HC_REMOVE);
-    hcc_leaf_string(hc, LC_PATH1, path);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_REMOVE);
+    hcc_leaf_string(trans, LC_PATH1, path);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -840,7 +847,7 @@
 }
 
 static int
-rc_remove(struct HostConf *hc __unused, struct HCHead *head)
+rc_remove(hctransaction_t trans __unused, struct HCHead *head)
 {
     struct HCLeaf *item;
     const char *path = NULL;
@@ -863,15 +870,16 @@
 int
 hc_mkdir(struct HostConf *hc __unused, const char *path, mode_t mode)
 {
+    hctransaction_t trans;
     struct HCHead *head;
 
     if (hc == NULL || hc->host == NULL)
 	return(mkdir(path, mode));
 
-    hcc_start_command(hc, HC_MKDIR);
-    hcc_leaf_string(hc, LC_PATH1, path);
-    hcc_leaf_int32(hc, LC_MODE, mode);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_MKDIR);
+    hcc_leaf_string(trans, LC_PATH1, path);
+    hcc_leaf_int32(trans, LC_MODE, mode);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -879,7 +887,7 @@
 }
 
 static int
-rc_mkdir(struct HostConf *hc __unused, struct HCHead *head)
+rc_mkdir(hctransaction_t trans __unused, struct HCHead *head)
 {
     struct HCLeaf *item;
     const char *path = NULL;
@@ -906,14 +914,15 @@
 int
 hc_rmdir(struct HostConf *hc, const char *path)
 {
+    hctransaction_t trans;
     struct HCHead *head;
 
     if (hc == NULL || hc->host == NULL)
 	return(rmdir(path));
 
-    hcc_start_command(hc, HC_RMDIR);
-    hcc_leaf_string(hc, LC_PATH1, path);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_RMDIR);
+    hcc_leaf_string(trans, LC_PATH1, path);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -921,7 +930,7 @@
 }
 
 static int
-rc_rmdir(struct HostConf *hc __unused, struct HCHead *head)
+rc_rmdir(hctransaction_t trans __unused, struct HCHead *head)
 {
     struct HCLeaf *item;
     const char *path = NULL;
@@ -944,16 +953,17 @@
 int
 hc_chown(struct HostConf *hc, const char *path, uid_t owner, gid_t group)
 {
+    hctransaction_t trans;
     struct HCHead *head;
 
     if (hc == NULL || hc->host == NULL)
 	return(chown(path, owner, group));
 
-    hcc_start_command(hc, HC_CHOWN);
-    hcc_leaf_string(hc, LC_PATH1, path);
-    hcc_leaf_int32(hc, LC_UID, owner);
-    hcc_leaf_int32(hc, LC_GID, group);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_CHOWN);
+    hcc_leaf_string(trans, LC_PATH1, path);
+    hcc_leaf_int32(trans, LC_UID, owner);
+    hcc_leaf_int32(trans, LC_GID, group);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -961,7 +971,7 @@
 }
 
 static int
-rc_chown(struct HostConf *hc __unused, struct HCHead *head)
+rc_chown(hctransaction_t trans __unused, struct HCHead *head)
 {
     struct HCLeaf *item;
     const char *path = NULL;
@@ -992,16 +1002,17 @@
 int
 hc_lchown(struct HostConf *hc, const char *path, uid_t owner, gid_t group)
 {
+    hctransaction_t trans;
     struct HCHead *head;
 
     if (hc == NULL || hc->host == NULL)
 	return(lchown(path, owner, group));
 
-    hcc_start_command(hc, HC_LCHOWN);
-    hcc_leaf_string(hc, LC_PATH1, path);
-    hcc_leaf_int32(hc, LC_UID, owner);
-    hcc_leaf_int32(hc, LC_GID, group);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_LCHOWN);
+    hcc_leaf_string(trans, LC_PATH1, path);
+    hcc_leaf_int32(trans, LC_UID, owner);
+    hcc_leaf_int32(trans, LC_GID, group);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -1009,7 +1020,7 @@
 }
 
 static int
-rc_lchown(struct HostConf *hc __unused, struct HCHead *head)
+rc_lchown(hctransaction_t trans __unused, struct HCHead *head)
 {
     struct HCLeaf *item;
     const char *path = NULL;
@@ -1040,15 +1051,16 @@
 int
 hc_chmod(struct HostConf *hc, const char *path, mode_t mode)
 {
+    hctransaction_t trans;
     struct HCHead *head;
 
     if (hc == NULL || hc->host == NULL)
 	return(chmod(path, mode));
 
-    hcc_start_command(hc, HC_CHMOD);
-    hcc_leaf_string(hc, LC_PATH1, path);
-    hcc_leaf_int32(hc, LC_MODE, mode);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_CHMOD);
+    hcc_leaf_string(trans, LC_PATH1, path);
+    hcc_leaf_int32(trans, LC_MODE, mode);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -1056,7 +1068,7 @@
 }
 
 static int
-rc_chmod(struct HostConf *hc __unused, struct HCHead *head)
+rc_chmod(hctransaction_t trans __unused, struct HCHead *head)
 {
     struct HCLeaf *item;
     const char *path = NULL;
@@ -1083,15 +1095,16 @@
 int
 hc_link(struct HostConf *hc, const char *name1, const char *name2)
 {
+    hctransaction_t trans;
     struct HCHead *head;
 
     if (hc == NULL || hc->host == NULL)
 	return(link(name1, name2));
 
-    hcc_start_command(hc, HC_LINK);
-    hcc_leaf_string(hc, LC_PATH1, name1);
-    hcc_leaf_string(hc, LC_PATH2, name2);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_LINK);
+    hcc_leaf_string(trans, LC_PATH1, name1);
+    hcc_leaf_string(trans, LC_PATH2, name2);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -1099,7 +1112,7 @@
 }
 
 static int
-rc_link(struct HostConf *hc __unused, struct HCHead *head)
+rc_link(hctransaction_t trans __unused, struct HCHead *head)
 {
     struct HCLeaf *item;
     const char *name1 = NULL;
@@ -1127,15 +1140,16 @@
 int
 hc_chflags(struct HostConf *hc, const char *path, u_long flags)
 {
+    hctransaction_t trans;
     struct HCHead *head;
 
     if (hc == NULL || hc->host == NULL)
 	return(chflags(path, flags));
 
-    hcc_start_command(hc, HC_CHFLAGS);
-    hcc_leaf_string(hc, LC_PATH1, path);
-    hcc_leaf_int64(hc, LC_FILEFLAGS, flags);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_CHFLAGS);
+    hcc_leaf_string(trans, LC_PATH1, path);
+    hcc_leaf_int64(trans, LC_FILEFLAGS, flags);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -1143,7 +1157,7 @@
 }
 
 static int
-rc_chflags(struct HostConf *hc __unused, struct HCHead *head)
+rc_chflags(hctransaction_t trans __unused, struct HCHead *head)
 {
     struct HCLeaf *item;
     const char *path = NULL;
@@ -1172,6 +1186,7 @@
 int
 hc_readlink(struct HostConf *hc, const char *path, char *buf, int bufsiz)
 {
+    hctransaction_t trans;
     struct HCHead *head;
     struct HCLeaf *item;
     int r;
@@ -1179,9 +1194,9 @@
     if (hc == NULL || hc->host == NULL)
 	return(readlink(path, buf, bufsiz));
 
-    hcc_start_command(hc, HC_READLINK);
-    hcc_leaf_string(hc, LC_PATH1, path);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_READLINK);
+    hcc_leaf_string(trans, LC_PATH1, path);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -1203,7 +1218,7 @@
 }
 
 static int
-rc_readlink(struct HostConf *hc, struct HCHead *head)
+rc_readlink(hctransaction_t trans, struct HCHead *head)
 {
     struct HCLeaf *item;
     const char *path = NULL;
@@ -1222,7 +1237,7 @@
     r = readlink(path, buf, sizeof(buf));
     if (r < 0)
 	return(-1);
-    hcc_leaf_data(hc, LC_DATA, buf, r);
+    hcc_leaf_data(trans, LC_DATA, buf, r);
     return(0);
 }
 
@@ -1232,15 +1247,16 @@
 mode_t
 hc_umask(struct HostConf *hc, mode_t numask)
 {
+    hctransaction_t trans;
     struct HCHead *head;
     struct HCLeaf *item;
 
     if (hc == NULL || hc->host == NULL)
 	return(umask(numask));
 
-    hcc_start_command(hc, HC_UMASK);
-    hcc_leaf_int32(hc, LC_MODE, numask);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_UMASK);
+    hcc_leaf_int32(trans, LC_MODE, numask);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return((mode_t)-1);
     if (head->error)
 	return((mode_t)-1);
@@ -1257,7 +1273,7 @@
 }
 
 static int
-rc_umask(struct HostConf *hc, struct HCHead *head)
+rc_umask(hctransaction_t trans, struct HCHead *head)
 {
     struct HCLeaf *item;
     mode_t numask = ~0666;
@@ -1270,7 +1286,7 @@
 	}
     }
     numask = umask(numask);
-    hcc_leaf_int32(hc, LC_MODE, numask);
+    hcc_leaf_int32(trans, LC_MODE, numask);
     return(0);
 }
 
@@ -1280,15 +1296,16 @@
 int
 hc_symlink(struct HostConf *hc, const char *name1, const char *name2)
 {
+    hctransaction_t trans;
     struct HCHead *head;
 
     if (hc == NULL || hc->host == NULL)
 	return(symlink(name1, name2));
 
-    hcc_start_command(hc, HC_SYMLINK);
-    hcc_leaf_string(hc, LC_PATH1, name1);
-    hcc_leaf_string(hc, LC_PATH2, name2);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_SYMLINK);
+    hcc_leaf_string(trans, LC_PATH1, name1);
+    hcc_leaf_string(trans, LC_PATH2, name2);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -1296,7 +1313,7 @@
 }
 
 static int
-rc_symlink(struct HostConf *hc __unused, struct HCHead *head)
+rc_symlink(hctransaction_t trans __unused, struct HCHead *head)
 {
     struct HCLeaf *item;
     const char *name1 = NULL;
@@ -1323,15 +1340,16 @@
 int
 hc_rename(struct HostConf *hc, const char *name1, const char *name2)
 {
+    hctransaction_t trans;
     struct HCHead *head;
   
     if (hc == NULL || hc->host == NULL)
 	return(rename(name1, name2));
 
-    hcc_start_command(hc, HC_RENAME);
-    hcc_leaf_string(hc, LC_PATH1, name1);
-    hcc_leaf_string(hc, LC_PATH2, name2);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_RENAME);
+    hcc_leaf_string(trans, LC_PATH1, name1);
+    hcc_leaf_string(trans, LC_PATH2, name2);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -1339,7 +1357,7 @@
 }
 
 static int
-rc_rename(struct HostConf *hc __unused, struct HCHead *head)
+rc_rename(hctransaction_t trans __unused, struct HCHead *head)
 {
     struct HCLeaf *item;
     const char *name1 = NULL;
@@ -1366,16 +1384,17 @@
 int
 hc_utimes(struct HostConf *hc, const char *path, const struct timeval *times)
 {
+    hctransaction_t trans;
     struct HCHead *head;
 
     if (hc == NULL || hc->host == NULL)
 	return(utimes(path, times));
 
-    hcc_start_command(hc, HC_UTIMES);
-    hcc_leaf_string(hc, LC_PATH1, path);
-    hcc_leaf_int64(hc, LC_ATIME, times[0].tv_sec);
-    hcc_leaf_int64(hc, LC_MTIME, times[1].tv_sec);
-    if ((head = hcc_finish_command(hc)) == NULL)
+    trans = hcc_start_command(hc, HC_UTIMES);
+    hcc_leaf_string(trans, LC_PATH1, path);
+    hcc_leaf_int64(trans, LC_ATIME, times[0].tv_sec);
+    hcc_leaf_int64(trans, LC_MTIME, times[1].tv_sec);
+    if ((head = hcc_finish_command(trans)) == NULL)
 	return(-1);
     if (head->error)
 	return(-1);
@@ -1383,7 +1402,7 @@
 }
 
 static int
-rc_utimes(struct HostConf *hc __unused, struct HCHead *head)
+rc_utimes(hctransaction_t trans __unused, struct HCHead *head)
 {
     struct HCLeaf *item;
     struct timeval times[2];
Index: hclink.h
===================================================================
RCS file: /home/cvs/src/bin/cpdup/hclink.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -L bin/cpdup/hclink.h -L bin/cpdup/hclink.h -u -r1.1 -r1.2
--- bin/cpdup/hclink.h
+++ bin/cpdup/hclink.h
@@ -1,8 +1,8 @@
 /*
  * HCLINK.H
  *
- * $MidnightBSD$
- * $DragonFly: src/bin/cpdup/hclink.h,v 1.1 2006/08/13 20:51:40 dillon Exp $
+ * $MIdnightBSD$
+ * $DragonFly: src/bin/cpdup/hclink.h,v 1.2 2008/04/10 22:09:08 dillon Exp $
  */
 
 #ifndef _HCLINK_H_
@@ -15,23 +15,47 @@
     void *data;
 };
 
+struct HostConf;
+
+typedef struct HCTransaction {
+    struct HCTransaction *next;
+    struct HostConf *hc;
+    u_int16_t	id;		/* assigned transaction id */
+    int		windex;		/* output buffer index */
+    enum { HCT_IDLE, HCT_SENT, HCT_REPLIED } state;
+#if USE_PTHREADS
+    pthread_t	tid;
+#endif
+    char	rbuf[65536];	/* input buffer */
+    char	wbuf[65536];	/* output buffer */
+} *hctransaction_t;
+
+#if USE_PTHREADS
+#define HCTHASH_SIZE	16
+#define HCTHASH_MASK	(HCTHASH_SIZE - 1)
+#endif
+
 struct HostConf {
-    char *host;		/* [user@]host */
-    int fdin;		/* pipe */
-    int fdout;		/* pipe */
-    int error;		/* permanent failure code */
-    pid_t pid;
-    int windex;		/* output buffer index */
+    char	*host;		/* [user@]host */
+    int		fdin;		/* pipe */
+    int		fdout;		/* pipe */
+    int		error;		/* permanent failure code */
+    pid_t	pid;
+    int		version;	/* cpdup protocol version */
     struct HCHostDesc *hostdescs;
-    char rbuf[65536];	/* output buffer */
-    char wbuf[65536];	/* output buffer */
+#if USE_PTHREADS
+    pthread_mutex_t read_mutex;
+    hctransaction_t hct_hash[HCTHASH_SIZE];
+#else
+    struct HCTransaction trans;
+#endif
 };
 
 struct HCHead {
     int32_t magic;		/* magic number / byte ordering */
     int32_t bytes;		/* size of packet */
     int16_t cmd;		/* command code */
-    int16_t id;			/* transaction id */
+    u_int16_t id;			/* transaction id */
     int32_t error;		/* error code (response) */
 };
 
@@ -57,7 +81,7 @@
 
 struct HCDesc {
     int16_t cmd;
-    int (*func)(struct HostConf *, struct HCHead *);
+    int (*func)(hctransaction_t, struct HCHead *);
 };
 
 /*
@@ -74,12 +98,12 @@
 int hcc_connect(struct HostConf *hc);
 int hcc_slave(int fdin, int fdout, struct HCDesc *descs, int count);
 
-void hcc_start_command(struct HostConf *hc, int16_t cmd);
-struct HCHead *hcc_finish_command(struct HostConf *hc);
-void hcc_leaf_string(struct HostConf *hc, int16_t leafid, const char *str);
-void hcc_leaf_data(struct HostConf *hc, int16_t leafid, const void *ptr, int bytes);
-void hcc_leaf_int32(struct HostConf *hc, int16_t leafid, int32_t value);
-void hcc_leaf_int64(struct HostConf *hc, int16_t leafid, int64_t value);
+hctransaction_t hcc_start_command(struct HostConf *hc, int16_t cmd);
+struct HCHead *hcc_finish_command(hctransaction_t trans);
+void hcc_leaf_string(hctransaction_t trans, int16_t leafid, const char *str);
+void hcc_leaf_data(hctransaction_t trans, int16_t leafid, const void *ptr, int bytes);
+void hcc_leaf_int32(hctransaction_t trans, int16_t leafid, int32_t value);
+void hcc_leaf_int64(hctransaction_t trans, int16_t leafid, int64_t value);
 
 int hcc_alloc_descriptor(struct HostConf *hc, void *ptr, int type);
 void *hcc_get_descriptor(struct HostConf *hc, int desc, int type);
Index: hcproto.h
===================================================================
RCS file: /home/cvs/src/bin/cpdup/hcproto.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -L bin/cpdup/hcproto.h -L bin/cpdup/hcproto.h -u -r1.1 -r1.2
--- bin/cpdup/hcproto.h
+++ bin/cpdup/hcproto.h
@@ -2,7 +2,7 @@
  * HCPROTO.H
  *
  * $MidnightBSD$
- * $DragonFly: src/bin/cpdup/hcproto.h,v 1.1 2006/08/13 20:51:40 dillon Exp $
+ * $DragonFly: src/bin/cpdup/hcproto.h,v 1.2 2008/04/10 22:09:08 dillon Exp $
  */
 
 #ifndef _HCPROTO_H_
@@ -58,6 +58,7 @@
 #define LC_DATA		(0x0025|LCF_BINARY)
 #define LC_TYPE		(0x0026|LCF_INT32)
 #define LC_BLKSIZE	(0x0027|LCF_INT32)
+#define LC_VERSION	(0x0028|LCF_INT32)
 
 #define XO_NATIVEMASK	3		/* passed through directly */
 #define XO_CREAT	0x00010000
Index: cpdup.1
===================================================================
RCS file: /home/cvs/src/bin/cpdup/cpdup.1,v
retrieving revision 1.1
retrieving revision 1.2
diff -L bin/cpdup/cpdup.1 -L bin/cpdup/cpdup.1 -u -r1.1 -r1.2
--- bin/cpdup/cpdup.1
+++ bin/cpdup/cpdup.1
@@ -2,10 +2,9 @@
 .\"    use and distribute based on the DragonFly copyright.  Supplied as-is,
 .\"    USE WITH EXTREME CAUTION.
 .\"
-.\"
 .\" $MidnightBSD$
-.\" $DragonFly: src/bin/cpdup/cpdup.1,v 1.24 2007/05/13 18:33:55 swildner Exp $
-.Dd October 28, 1999
+.\" $DragonFly: src/bin/cpdup/cpdup.1,v 1.28 2008/04/10 22:09:08 dillon Exp $
+.Dd March 22, 2008
 .Dt CPDUP 1
 .Os
 .Sh NAME
@@ -20,6 +19,7 @@
 .Op Fl s0
 .Op Fl i0
 .Op Fl j0
+.Op Fl p<num>
 .Op Fl q
 .Op Fl o
 .Op Fl m
@@ -31,6 +31,7 @@
 .Fl M
 .Ar file
 .Oc
+.Op Fl V
 .Op Fl S
 .Op Fl k
 .Oo
@@ -94,11 +95,17 @@
 .It Fl s0
 Disable the disallow-file-replaces-directory safety feature.  This
 safety feature is enabled by default to prevent user mistakes from blowing
-away everything accidently.
+away everything accidentally.
 .It Fl i0
 Do not request confirmation when removing something.
 .It Fl j0
 Do not try to recreate CHR or BLK devices.
+.It Fl l
+Line buffer verbose output.
+.It Fl p<number>
+Use threaded transactions with up to the specified number of threads.
+This typically improves operation when a remote host specification is
+given.
 .It Fl q
 Quiet operation
 .It Fl o
@@ -122,8 +129,8 @@
 via
 .Ar path
 is identical to the source.
-Note that a remote host specification should not be used in this option,
-but the path will be relative to the target machine.
+Note that a remote host specification should not be used for this option's
+path, but the path will be relative to the target machine.
 .Pp
 This allows one to use
 .Nm
@@ -146,10 +153,25 @@
 .Nm
 must record the paths for all files it encounters while it operates
 and it is possible that you may run the process out of memory.
+.Pp
+The file found vi the hardlink path will be byte-by-byte compared with the
+source if the
+.Fl V
+or
+.Fl f
+option is also used, otherwise only the stat info is checked to determine
+whether it matches the source.
 .It Fl M Ar file
 Works the same as
 .Fl m
 but allows you to specify the name of the MD5 checkfile.
+.It Fl V
+This forces the contents of regular files to be verified, even if the
+files appear to the be the same.  Whereas the
+.Fl f
+(force) option forces a copy regardless, this option will avoid rewriting
+the target if everything matches and the contents are verified to be the
+same.
 .It Fl S
 This places
 .Nm
Index: fsmid.c
===================================================================
RCS file: /home/cvs/src/bin/cpdup/fsmid.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L bin/cpdup/fsmid.c -L bin/cpdup/fsmid.c -u -r1.1 -r1.2
--- bin/cpdup/fsmid.c
+++ bin/cpdup/fsmid.c
@@ -4,8 +4,8 @@
  * (c) Copyright 1997-1999,2006 by Matthew Dillon.  Permission to
  *     use and distribute based on the FreeBSD copyright.
  *
- * $MidnightBSD$
- * $DragonFly: src/bin/cpdup/fsmid.c,v 1.1 2006/04/25 21:30:45 dillon Exp $
+ * $MIdnightBSD$
+ * $DragonFly: src/bin/cpdup/fsmid.c,v 1.2 2008/03/22 18:09:16 dillon Exp $
  */
 
 #include "cpdup.h"
@@ -123,7 +123,7 @@
 	     * extracting fid_Name - name may contain embedded control 
 	     * characters.
 	     */
-	    CountReadBytes += nlen+1;
+	    CountSourceReadBytes += nlen+1;
 	    node->fid_Name = fextract(fi, nlen, &c, EOF);
 	    if (c != '\n') {
 		fprintf(stderr, "Error parsing FSMID Cache: %s (%c)\n", FSMIDDCache, c);
Index: misc.c
===================================================================
RCS file: /home/cvs/src/bin/cpdup/misc.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L bin/cpdup/misc.c -L bin/cpdup/misc.c -u -r1.1 -r1.2
--- bin/cpdup/misc.c
+++ bin/cpdup/misc.c
@@ -1,8 +1,8 @@
 /*
  * MISC.C
  *
- * $MidnightBSD$
- * $DragonFly: src/bin/cpdup/misc.c,v 1.8 2006/09/16 18:18:05 dillon Exp $
+ * $MIdnightBSD$
+ * $DragonFly: src/bin/cpdup/misc.c,v 1.11 2008/04/10 22:09:08 dillon Exp $
  */
 
 #include "cpdup.h"
@@ -97,6 +97,9 @@
 	     "    -I          display performance summary\n"
 	     "    -f          force update even if files look the same\n"
 	     "    -i0         do NOT confirm when removing something\n"
+	     "    -l          force line-buffered stdout/stderr\n"
+	     "    -pN         N parallel transactions for for remote\n"
+	     "                source or destination\n"
 	     "    -s0         disable safeties - allow files to overwrite directories\n"
 	     "    -q          quiet operation\n"
 	     "    -o          do not remove any files, just overwrite/add\n"
@@ -108,10 +111,14 @@
 	     "                copying if the compare fails\n"
 	     "    -M file     -m+specify MD5 checkfile, else .MD5_CHECKSUMS\n"
 	     "                copy if md5 check fails\n"
+	     "    -H path     hardlink from path to target instead of copying\n"
+	     "                source to target, if source matches path.\n"
+	     "	  -V          verify file contents even if they appear\n"
+	     "                to be the same.\n"
 #endif
 	     "    -x          use .cpignore as exclusion file\n"
 	     "    -X file     specify exclusion file\n"
-	     " Version 1.07 by Matt Dillon and Dima Ruban\n"
+	     " Version 1.09 by Matt Dillon and Dima Ruban\n"
 	);
 	exit(0);
     } else {
Index: cpdup.h
===================================================================
RCS file: /home/cvs/src/bin/cpdup/cpdup.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -L bin/cpdup/cpdup.h -L bin/cpdup/cpdup.h -u -r1.1 -r1.2
--- bin/cpdup/cpdup.h
+++ bin/cpdup/cpdup.h
@@ -2,7 +2,7 @@
  * CPDUP.H
  *
  * $MidnightBSD$
- * $DragonFly: src/bin/cpdup/cpdup.h,v 1.5 2006/08/18 01:13:51 dillon Exp $
+ * $DragonFly: src/bin/cpdup/cpdup.h,v 1.7 2008/04/10 22:09:08 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -26,6 +26,9 @@
 #ifndef NOMD5
 #include <md5.h>
 #endif
+#if USE_PTHREADS
+#include <pthread.h>
+#endif
 
 void logstd(const char *ctl, ...);
 void logerr(const char *ctl, ...);
@@ -48,7 +51,11 @@
 extern int64_t CountSourceBytes;
 extern int64_t CountSourceItems;
 extern int64_t CountCopiedItems;
-extern int64_t CountReadBytes;
+extern int64_t CountSourceReadBytes;
+extern int64_t CountTargetReadBytes;
 extern int64_t CountWriteBytes;
 extern int64_t CountRemovedItems;
 
+#if USE_PTHREADS
+extern pthread_mutex_t MasterMutex;
+#endif
Index: hclink.c
===================================================================
RCS file: /home/cvs/src/bin/cpdup/hclink.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L bin/cpdup/hclink.c -L bin/cpdup/hclink.c -u -r1.1 -r1.2
--- bin/cpdup/hclink.c
+++ bin/cpdup/hclink.c
@@ -4,24 +4,15 @@
  * This module implements a simple remote control protocol
  *
  * $MidnightBSD$
- * $DragonFly: src/bin/cpdup/hclink.c,v 1.4 2007/01/17 02:34:10 pavalos Exp $
+ * $DragonFly: src/bin/cpdup/hclink.c,v 1.5 2008/04/10 22:09:08 dillon Exp $
  */
 
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <assert.h>
-#include <errno.h>
-
+#include "cpdup.h"
 #include "hclink.h"
 #include "hcproto.h"
 
-static struct HCHead *hcc_read_command(struct HostConf *hc);
+static struct HCHead *hcc_read_command(hctransaction_t trans, int anypkt);
+static void hcc_start_reply(hctransaction_t trans, struct HCHead *rhead);
 
 int
 hcc_connect(struct HostConf *hc)
@@ -67,7 +58,7 @@
 }
 
 static int
-rc_badop(struct HostConf *hc __unused, struct HCHead *head)
+rc_badop(hctransaction_t trans __unused, struct HCHead *head)
 {
     head->error = EOPNOTSUPP;
     return(0);
@@ -79,12 +70,14 @@
     struct HostConf hcslave;
     struct HCHead *head;
     struct HCHead *whead;
-    int (*dispatch[256])(struct HostConf *, struct HCHead *);
+    struct HCTransaction trans;
+    int (*dispatch[256])(hctransaction_t, struct HCHead *);
     int aligned_bytes;
     int i;
     int r;
 
     bzero(&hcslave, sizeof(hcslave));
+    bzero(&trans, sizeof(trans));
     for (i = 0; i < count; ++i) {
 	struct HCDesc *desc = &descs[i];
 	assert(desc->cmd >= 0 && desc->cmd < 256);
@@ -96,6 +89,7 @@
     }
     hcslave.fdin = fdin;
     hcslave.fdout = fdout;
+    trans.hc = &hcslave;
 
     /*
      * Process commands on fdin and write out results on fdout
@@ -104,7 +98,7 @@
 	/*
 	 * Get the command
 	 */
-	head = hcc_read_command(&hcslave);
+	head = hcc_read_command(&trans, 1);
 	if (head == NULL)
 	    break;
 
@@ -112,8 +106,10 @@
 	 * Start the reply and dispatch, then process the return code.
 	 */
 	head->error = 0;
-	hcc_start_command(&hcslave, head->cmd | HCF_REPLY);
-	r = dispatch[head->cmd & 255](&hcslave, head);
+	hcc_start_reply(&trans, head);
+
+	r = dispatch[head->cmd & 255](&trans, head);
+
 	switch(r) {
 	case -2:
 		head->error = EINVAL;
@@ -131,10 +127,10 @@
 	/*
 	 * Write out the reply
 	 */
-	whead = (void *)hcslave.wbuf;
-	whead->bytes = hcslave.windex;
+	whead = (void *)trans.wbuf;
+	whead->bytes = trans.windex;
 	whead->error = head->error;
-	aligned_bytes = HCC_ALIGN(hcslave.windex);
+	aligned_bytes = HCC_ALIGN(trans.windex);
 #ifdef DEBUG
 	hcc_debug_dump(whead);
 #endif
@@ -147,53 +143,175 @@
 /*
  * This reads a command from fdin, fixes up the byte ordering, and returns
  * a pointer to HCHead.
+ *
+ * If id is -1 we do not match response id's
  */
 static
 struct HCHead *
-hcc_read_command(struct HostConf *hc)
+hcc_read_command(hctransaction_t trans, int anypkt)
 {
-    struct HCHead *head;
+    struct HostConf *hc = trans->hc;
+    hctransaction_t fill;
+    struct HCHead tmp;
     int aligned_bytes;
     int n;
     int r;
 
-    n = 0;
-    while (n < (int)sizeof(struct HCHead)) {
-	r = read(hc->fdin, hc->rbuf + n, sizeof(struct HCHead) - n);
-	if (r <= 0)
-	    return(NULL);
-	n += r;
-    }
-    head = (void *)hc->rbuf;
-    assert(head->bytes >= (int)sizeof(*head) && head->bytes < 65536);
-    assert(head->magic == HCMAGIC);
-    aligned_bytes = HCC_ALIGN(head->bytes);
-    while (n < aligned_bytes) {
-	r = read(hc->fdin, hc->rbuf + n, aligned_bytes - n);
-	if (r <= 0)
-	    return(NULL);
-	n += r;
+#if USE_PTHREADS
+    /*
+     * Shortcut if the reply has already been read in by another thread.
+     */
+    if (trans->state == HCT_REPLIED) {
+	return((void *)trans->rbuf);
     }
+    pthread_mutex_unlock(&MasterMutex);
+    pthread_mutex_lock(&hc->read_mutex);
+#endif
+    while (trans->state != HCT_REPLIED) {
+	n = 0;
+	while (n < (int)sizeof(struct HCHead)) {
+	    r = read(hc->fdin, (char *)&tmp + n, sizeof(struct HCHead) - n);
+	    if (r <= 0)
+		goto fail;
+	    n += r;
+	}
+
+	assert(tmp.bytes >= (int)sizeof(tmp) && tmp.bytes < 65536);
+	assert(tmp.magic == HCMAGIC);
+
+	if (anypkt == 0 && tmp.id != trans->id && trans->hc->version > 0) {
+#if USE_PTHREADS
+	    for (fill = trans->hc->hct_hash[tmp.id & HCTHASH_MASK];
+		 fill;
+		 fill = fill->next)
+	    {
+		if (fill->state == HCT_SENT && fill->id == tmp.id)
+			break;
+	    }
+	    if (fill == NULL)
+#endif
+	    {
+		fprintf(stderr, 
+			"cpdup hlink protocol error with %s (%04x %04x)\n",
+			trans->hc->host, trans->id, tmp.id);
+		exit(1);
+	    }
+	} else {
+	    fill = trans;
+	}
+
+	bcopy(&tmp, fill->rbuf, n);
+	aligned_bytes = HCC_ALIGN(tmp.bytes);
+
+	while (n < aligned_bytes) {
+	    r = read(hc->fdin, fill->rbuf + n, aligned_bytes - n);
+	    if (r <= 0)
+		goto fail;
+	    n += r;
+	}
 #ifdef DEBUG
-    hcc_debug_dump(head);
+	hcc_debug_dump(head);
+#endif
+	fill->state = HCT_REPLIED;
+    }
+#if USE_PTHREADS
+    pthread_mutex_lock(&MasterMutex);
+    pthread_mutex_unlock(&hc->read_mutex);
+#endif
+    return((void *)trans->rbuf);
+fail:
+#if USE_PTHREADS
+    pthread_mutex_lock(&MasterMutex);
+    pthread_mutex_unlock(&hc->read_mutex);
 #endif
-    return(head);
+    return(NULL);
+}
+
+#if USE_PTHREADS
+
+static
+hctransaction_t
+hcc_get_trans(struct HostConf *hc)
+{
+    hctransaction_t trans;
+    hctransaction_t scan;
+    pthread_t tid = pthread_self();
+    int i;
+
+    i = ((intptr_t)tid >> 7) & HCTHASH_MASK;
+
+    for (trans = hc->hct_hash[i]; trans; trans = trans->next) {
+	if (trans->tid == tid)
+		break;
+    }
+    if (trans == NULL) {
+	trans = malloc(sizeof(*trans));
+	bzero(trans, sizeof(*trans));
+	trans->tid = tid;
+	trans->id = i;
+	do {
+		for (scan = hc->hct_hash[i]; scan; scan = scan->next) {
+			if (scan->id == trans->id) {
+				trans->id += HCTHASH_SIZE;
+				break;
+			}
+		}
+	} while (scan != NULL);
+
+	trans->next = hc->hct_hash[i];
+	hc->hct_hash[i] = trans;
+    }
+    return(trans);
+}
+
+#else
+
+static
+hctransaction_t
+hcc_get_trans(struct HostConf *hc)
+{
+    return(&hc->trans);
 }
 
+#endif
+
 /*
  * Initialize for a new command
  */
-void
+hctransaction_t
 hcc_start_command(struct HostConf *hc, int16_t cmd)
 {
-    struct HCHead *whead = (void *)hc->wbuf;
+    struct HCHead *whead;
+    hctransaction_t trans;
 
+    trans = hcc_get_trans(hc);
+
+    whead = (void *)trans->wbuf;
     whead->magic = HCMAGIC;
     whead->bytes = 0;
     whead->cmd = cmd;
-    whead->id = 0;
+    whead->id = trans->id;
     whead->error = 0;
-    hc->windex = sizeof(*whead);
+
+    trans->windex = sizeof(*whead);
+    trans->hc = hc;
+    trans->state = HCT_IDLE;
+
+    return(trans);
+}
+
+static void
+hcc_start_reply(hctransaction_t trans, struct HCHead *rhead)
+{
+    struct HCHead *whead = (void *)trans->wbuf;
+
+    whead->magic = HCMAGIC;
+    whead->bytes = 0;
+    whead->cmd = rhead->cmd | HCF_REPLY;
+    whead->id = rhead->id;
+    whead->error = 0;
+
+    trans->windex = sizeof(*whead);
 }
 
 /*
@@ -201,16 +319,20 @@
  * Return the HCHead of the reply.
  */
 struct HCHead *
-hcc_finish_command(struct HostConf *hc)
+hcc_finish_command(hctransaction_t trans)
 {
     struct HCHead *whead;
     struct HCHead *rhead;
     int aligned_bytes;
+    int16_t wcmd;
+
+    whead = (void *)trans->wbuf;
+    whead->bytes = trans->windex;
+    aligned_bytes = HCC_ALIGN(trans->windex);
 
-    whead = (void *)hc->wbuf;
-    whead->bytes = hc->windex;
-    aligned_bytes = HCC_ALIGN(hc->windex);
-    if (write(hc->fdout, whead, aligned_bytes) != aligned_bytes) {
+    trans->state = HCT_SENT;
+
+    if (write(trans->hc->fdout, whead, aligned_bytes) != aligned_bytes) {
 #ifdef __error
 	*__error = EIO;
 #else
@@ -218,20 +340,28 @@
 #endif
 	if (whead->cmd < 0x0010)
 		return(NULL);
-	fprintf(stderr, "cpdup lost connection to %s\n", hc->host);
+	fprintf(stderr, "cpdup lost connection to %s\n", trans->hc->host);
 	exit(1);
     }
-    if ((rhead = hcc_read_command(hc)) == NULL) {
+
+    wcmd = whead->cmd;
+
+    /*
+     * whead is invalid when we call hcc_read_command() because
+     * we may switch to another thread.
+     */
+    if ((rhead = hcc_read_command(trans, 0)) == NULL) {
 #ifdef __error
 	*__error = EIO;
 #else
 	errno = EIO;
 #endif
-	if (whead->cmd < 0x0010)
+	if (wcmd < 0x0010)
 		return(NULL);
-	fprintf(stderr, "cpdup lost connection to %s\n", hc->host);
+	fprintf(stderr, "cpdup lost connection to %s\n", trans->hc->host);
 	exit(1);
     }
+
     if (rhead->error) {
 #ifdef __error
 	*__error = rhead->error;
@@ -243,60 +373,60 @@
 }
 
 void
-hcc_leaf_string(struct HostConf *hc, int16_t leafid, const char *str)
+hcc_leaf_string(hctransaction_t trans, int16_t leafid, const char *str)
 {
     struct HCLeaf *item;
     int bytes = strlen(str) + 1;
 
-    item = (void *)(hc->wbuf + hc->windex);
-    assert(hc->windex + sizeof(*item) + bytes < 65536);
+    item = (void *)(trans->wbuf + trans->windex);
+    assert(trans->windex + sizeof(*item) + bytes < 65536);
     item->leafid = leafid;
     item->reserved = 0;
     item->bytes = sizeof(*item) + bytes;
     bcopy(str, item + 1, bytes);
-    hc->windex = HCC_ALIGN(hc->windex + item->bytes);
+    trans->windex = HCC_ALIGN(trans->windex + item->bytes);
 }
 
 void
-hcc_leaf_data(struct HostConf *hc, int16_t leafid, const void *ptr, int bytes)
+hcc_leaf_data(hctransaction_t trans, int16_t leafid, const void *ptr, int bytes)
 {
     struct HCLeaf *item;
 
-    item = (void *)(hc->wbuf + hc->windex);
-    assert(hc->windex + sizeof(*item) + bytes < 65536);
+    item = (void *)(trans->wbuf + trans->windex);
+    assert(trans->windex + sizeof(*item) + bytes < 65536);
     item->leafid = leafid;
     item->reserved = 0;
     item->bytes = sizeof(*item) + bytes;
     bcopy(ptr, item + 1, bytes);
-    hc->windex = HCC_ALIGN(hc->windex + item->bytes);
+    trans->windex = HCC_ALIGN(trans->windex + item->bytes);
 }
 
 void
-hcc_leaf_int32(struct HostConf *hc, int16_t leafid, int32_t value)
+hcc_leaf_int32(hctransaction_t trans, int16_t leafid, int32_t value)
 {
     struct HCLeaf *item;
 
-    item = (void *)(hc->wbuf + hc->windex);
-    assert(hc->windex + sizeof(*item) + sizeof(value) < 65536);
+    item = (void *)(trans->wbuf + trans->windex);
+    assert(trans->windex + sizeof(*item) + sizeof(value) < 65536);
     item->leafid = leafid;
     item->reserved = 0;
     item->bytes = sizeof(*item) + sizeof(value);
     *(int32_t *)(item + 1) = value;
-    hc->windex = HCC_ALIGN(hc->windex + item->bytes);
+    trans->windex = HCC_ALIGN(trans->windex + item->bytes);
 }
 
 void
-hcc_leaf_int64(struct HostConf *hc, int16_t leafid, int64_t value)
+hcc_leaf_int64(hctransaction_t trans, int16_t leafid, int64_t value)
 {
     struct HCLeaf *item;
 
-    item = (void *)(hc->wbuf + hc->windex);
-    assert(hc->windex + sizeof(*item) + sizeof(value) < 65536);
+    item = (void *)(trans->wbuf + trans->windex);
+    assert(trans->windex + sizeof(*item) + sizeof(value) < 65536);
     item->leafid = leafid;
     item->reserved = 0;
     item->bytes = sizeof(*item) + sizeof(value);
     *(int64_t *)(item + 1) = value;
-    hc->windex = HCC_ALIGN(hc->windex + item->bytes);
+    trans->windex = HCC_ALIGN(trans->windex + item->bytes);
 }
 
 int
Index: PORTING
===================================================================
RCS file: /home/cvs/src/bin/cpdup/PORTING,v
retrieving revision 1.1
retrieving revision 1.2
diff -L bin/cpdup/PORTING -L bin/cpdup/PORTING -u -r1.1 -r1.2
--- bin/cpdup/PORTING
+++ bin/cpdup/PORTING
@@ -1,4 +1,4 @@
-$MidnightBSD$
+$MIdnightBSD$
 $DragonFly: src/bin/cpdup/PORTING,v 1.1 2006/09/16 21:57:08 dillon Exp $
 
 				PORTING CPDUP
Index: md5.c
===================================================================
RCS file: /home/cvs/src/bin/cpdup/md5.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L bin/cpdup/md5.c -L bin/cpdup/md5.c -u -r1.1 -r1.2
--- bin/cpdup/md5.c
+++ bin/cpdup/md5.c
@@ -5,8 +5,8 @@
  *     use and distribute based on the FreeBSD copyright.  Supplied as-is,
  *     USE WITH EXTREME CAUTION.
  *
- * $MidnightBSD$
- * $DragonFly: src/bin/cpdup/md5.c,v 1.1 2006/04/25 21:30:45 dillon Exp $
+ * $MIdnightBSD$
+ * $DragonFly: src/bin/cpdup/md5.c,v 1.2 2008/03/22 18:09:16 dillon Exp $
  */
 
 #include "cpdup.h"
@@ -20,7 +20,7 @@
 
 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 *doMD5File(const char *filename, char *buf, int is_target);
 
 static char *MD5SCache;		/* cache source directory name */
 static MD5Node *MD5Base;
@@ -127,7 +127,7 @@
 	     * extracting md_Name - name may contain embedded control 
 	     * characters.
 	     */
-	    CountReadBytes += nlen+1;
+	    CountSourceReadBytes += nlen+1;
 	    node->md_Name = fextract(fi, nlen, &c, EOF);
 	    if (c != '\n') {
 		fprintf(stderr, "Error parsing MD5 Cache: %s (%c)\n", MD5SCache, c);
@@ -206,7 +206,7 @@
      */
 
     if (dpath == NULL) {
-	char *scode = doMD5File(spath, NULL);
+	char *scode = doMD5File(spath, NULL, 0);
 
 	r = 0;
 	if (node->md_Code == NULL) {
@@ -229,16 +229,16 @@
      */
 
     if (node->md_Code == NULL) {
-	node->md_Code = doMD5File(spath, NULL);
+	node->md_Code = doMD5File(spath, NULL, 0);
 	MD5SCacheDirty = 1;
     }
 
-    dcode = doMD5File(dpath, NULL);
+    dcode = doMD5File(dpath, NULL, 1);
     if (dcode) {
 	if (strcmp(node->md_Code, dcode) == 0) {
 	    r = 0;
 	} else {
-	    char *scode = doMD5File(spath, NULL);
+	    char *scode = doMD5File(spath, NULL, 0);
 
 	    if (strcmp(node->md_Code, scode) == 0) {
 		    free(scode);
@@ -256,15 +256,16 @@
 }
 
 char *
-doMD5File(const char *filename, char *buf)
+doMD5File(const char *filename, char *buf, int is_target)
 {
     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;
+	    u_int64_t size = st.st_size;
+	    if (is_target)
+		    CountTargetReadBytes += size;
+	    else
+		    CountSourceReadBytes += size;
 	}
     }
     return MD5File(filename, buf);
Index: Makefile
===================================================================
RCS file: /home/cvs/src/bin/cpdup/Makefile,v
retrieving revision 1.1
retrieving revision 1.2
diff -L bin/cpdup/Makefile -L bin/cpdup/Makefile -u -r1.1 -r1.2
--- bin/cpdup/Makefile
+++ bin/cpdup/Makefile
@@ -1,10 +1,14 @@
 # $MidnightBSD$
-# $DragonFly: src/bin/cpdup/Makefile,v 1.6 2006/08/13 20:51:40 dillon Exp $
+# $DragonFly: src/bin/cpdup/Makefile,v 1.7 2008/04/10 22:09:08 dillon Exp $
 #
 
 PROG=	cpdup
 SRCS=	cpdup.c hcproto.c hclink.c misc.c fsmid.c
 
+.if !defined(NOPTHREADS)
+CFLAGS += -DUSE_PTHREADS=1 -pthread
+.endif
+
 .if !defined(NOMD5)
 SRCS+=	md5.c
 .endif
Index: cpdup.c
===================================================================
RCS file: /home/cvs/src/bin/cpdup/cpdup.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -L bin/cpdup/cpdup.c -L bin/cpdup/cpdup.c -u -r1.1 -r1.2
--- bin/cpdup/cpdup.c
+++ bin/cpdup/cpdup.c
@@ -46,7 +46,7 @@
  *	  the 'previous' version (supplied with -H path).
  *
  * $MidnightBSD$
- * $DragonFly: src/bin/cpdup/cpdup.c,v 1.18 2006/09/21 04:09:28 dillon Exp $
+ * $DragonFly: src/bin/cpdup/cpdup.c,v 1.20 2008/04/10 22:09:08 dillon Exp $
  */
 
 /*-
@@ -60,7 +60,7 @@
 #include "hclink.h"
 #include "hcproto.h"
 
-#define HSIZE	16384
+#define HSIZE	8192
 #define HMASK	(HSIZE-1)
 #define HLSIZE	8192
 #define HLMASK	(HLSIZE - 1)
@@ -90,6 +90,19 @@
     char name[0];
 };
 
+typedef struct copy_info {
+	char *spath;
+	char *dpath;
+	dev_t sdevNo;
+	dev_t ddevNo;
+#ifdef USE_PTHREADS
+	struct copy_info *parent;
+	pthread_cond_t cond;
+	int children;
+	int r;
+#endif
+} *copy_info_t;
+
 struct hlink *hltable[HLSIZE];
 
 void RemoveRecur(const char *dpath, dev_t devNo);
@@ -99,13 +112,14 @@
 static struct hlink *hltlookup(struct stat *);
 static struct hlink *hltadd(struct stat *, const char *);
 static char *checkHLPath(struct stat *st, const char *spath, const char *dpath);
+static int validate_check(const char *spath, const char *dpath);
 static int shash(const char *s);
 static void hltdelete(struct hlink *);
 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);
-int DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo);
+static int DoCopy(copy_info_t info);
 
 int AskConfirmation = 1;
 int SafetyOpt = 1;
@@ -120,6 +134,9 @@
 int SlaveOpt;
 int EnableDirectoryRetries;
 int DstBaseLen;
+int ValidateOpt;
+int CurParallel;
+int MaxParallel = -1;
 char IOBuf1[65536];
 char IOBuf2[65536];
 const char *UseCpFile;
@@ -130,13 +147,19 @@
 int64_t CountSourceBytes;
 int64_t CountSourceItems;
 int64_t CountCopiedItems;
-int64_t CountReadBytes;
+int64_t CountSourceReadBytes;
+int64_t CountTargetReadBytes;
 int64_t CountWriteBytes;
 int64_t CountRemovedItems;
+int64_t CountLinkedItems;
 
 struct HostConf SrcHost;
 struct HostConf DstHost;
 
+#if USE_PTHREADS
+pthread_mutex_t MasterMutex;
+#endif
+
 int
 main(int ac, char **av)
 {
@@ -145,9 +168,17 @@
     char *dst = NULL;
     char *ptr;
     struct timeval start;
+    struct copy_info info;
 
     signal(SIGPIPE, SIG_IGN);
 
+#if USE_PTHREADS
+    pthread_mutex_init(&SrcHost.read_mutex, NULL);
+    pthread_mutex_init(&DstHost.read_mutex, NULL);
+    pthread_mutex_init(&MasterMutex, NULL);
+    pthread_mutex_lock(&MasterMutex);
+#endif
+
     gettimeofday(&start, NULL);
     for (i = 1; i < ac; ++i) {
 	int v = 1;
@@ -179,6 +210,13 @@
 	    if (*ptr >= '0' && *ptr <= '9')
 		VerboseOpt = strtol(ptr, NULL, 0);
 	    break;
+	case 'l':
+	    setlinebuf(stdout);
+	    setlinebuf(stderr);
+	    break;
+	case 'V':
+	    ValidateOpt = v;
+	    break;
 	case 'I':
 	    SummaryOpt = v;
 	    break;
@@ -206,6 +244,9 @@
 	case 'j':
 	    DeviceOpt = v;
 	    break;
+	case 'p':
+	    MaxParallel = v;
+	    break;
 	case 's':
 	    SafetyOpt = v;
 	    break;
@@ -283,12 +324,28 @@
 	fatal(NULL);
 	/* not reached */
     }
+#if USE_PTHREADS
+    info.r = 0;
+    info.children = 0;
+    pthread_cond_init(&info.cond, NULL);
+#endif
     if (dst) {
 	DstBaseLen = strlen(dst);
-	i = DoCopy(src, dst, (dev_t)-1, (dev_t)-1);
+	info.spath = src;
+	info.dpath = dst;
+	info.sdevNo = (dev_t)-1;
+	info.ddevNo = (dev_t)-1;
+	i = DoCopy(&info);
     } else {
-	i = DoCopy(src, NULL, (dev_t)-1, (dev_t)-1);
+	info.spath = src;
+	info.dpath = NULL;
+	info.sdevNo = (dev_t)-1;
+	info.ddevNo = (dev_t)-1;
+	i = DoCopy(&info);
     }
+#if USE_PTHREADS
+    pthread_cond_destroy(&info.cond);
+#endif
 #ifndef NOMD5
     md5_flush();
 #endif
@@ -299,28 +356,35 @@
 	struct timeval end;
 
 	gettimeofday(&end, NULL);
+#if 0
+	/* don't count stat's in our byte statistics */
 	CountSourceBytes += sizeof(struct stat) * CountSourceItems;
-	CountReadBytes += sizeof(struct stat) * CountSourceItems;
+	CountSourceReadBytes += sizeof(struct stat) * CountSourceItems;
 	CountWriteBytes +=  sizeof(struct stat) * CountCopiedItems;
 	CountWriteBytes +=  sizeof(struct stat) * CountRemovedItems;
+#endif
 
 	duration = end.tv_sec - start.tv_sec;
 	duration *= 1000000;
 	duration += end.tv_usec - start.tv_usec;
 	if (duration == 0) duration = 1;
 	logstd("cpdup completed successfully\n");
-	logstd("%lld bytes source %lld bytes read %lld bytes written (%.1fX speedup)\n",
+	logstd("%lld bytes source, %lld src bytes read, %lld tgt bytes read\n"
+	       "%lld bytes written (%.1fX speedup)\n",
 	    (long long)CountSourceBytes,
-	    (long long)CountReadBytes,
+	    (long long)CountSourceReadBytes,
+	    (long long)CountTargetReadBytes,
 	    (long long)CountWriteBytes,
-	    ((double)CountSourceBytes * 2.0) / ((double)(CountReadBytes + CountWriteBytes)));
- 	logstd("%lld source items %lld items copied %lld things deleted\n",
+	    ((double)CountSourceBytes * 2.0) / ((double)(CountSourceReadBytes + CountTargetReadBytes + CountWriteBytes)));
+ 	logstd("%lld source items, %lld items copied, %lld items linked, "
+	       "%lld things deleted\n",
 	    (long long)CountSourceItems,
 	    (long long)CountCopiedItems,
+	    (long long)CountLinkedItems,
 	    (long long)CountRemovedItems);
 	logstd("%.1f seconds %5d Kbytes/sec synced %5d Kbytes/sec scanned\n",
 	    (float)duration / (float)1000000,
-	    (long)((long)1000000 * (CountReadBytes + CountWriteBytes) / duration  / 1024.0),
+	    (long)((long)1000000 * (CountSourceReadBytes + CountTargetReadBytes + CountWriteBytes) / duration  / 1024.0),
 	    (long)((long)1000000 * CountSourceBytes / duration / 1024.0));
     }
     exit((i == 0) ? 0 : 1);
@@ -396,9 +460,7 @@
 {
     struct stat sthl;
     char *hpath;
-    int fd1;
-    int fd2;
-    int good;
+    int error;
 
     asprintf(&hpath, "%s%s", UseHLPath, dpath + DstBaseLen);
 
@@ -416,30 +478,11 @@
     }
 
     /*
-     * If ForceOpt is set we have to compare the files
+     * If ForceOpt or ValidateOpt is set we have to compare the files
      */
-    if (ForceOpt) {
-	fd1 = hc_open(&SrcHost, spath, O_RDONLY, 0);
-	fd2 = hc_open(&DstHost, hpath, O_RDONLY, 0);
-	good = 0;
-
-	if (fd1 >= 0 && fd2 >= 0) {
-	    int n;
-
-	    while ((n = hc_read(&SrcHost, fd1, IOBuf1, sizeof(IOBuf1))) > 0) {
-		if (hc_read(&DstHost, fd2, IOBuf2, sizeof(IOBuf2)) != n)
-		    break;
-		if (bcmp(IOBuf1, IOBuf2, n) != 0)
-		    break;
-	    }
-	    if (n == 0)
-		good = 1;
-	}
-	if (fd1 >= 0)
-	    hc_close(&SrcHost, fd1);
-	if (fd2 >= 0)
-	    hc_close(&DstHost, fd2);
-	if (good == 0) {
+    if (ForceOpt || ValidateOpt) {
+	error = validate_check(spath, hpath);
+	if (error) {
 	    free(hpath);
 	    hpath = NULL;
 	}
@@ -447,37 +490,101 @@
     return(hpath);
 }
 
+/*
+ * Return 0 if the contents of the file <spath> matches the contents of
+ * the file <dpath>.
+ */
+static int
+validate_check(const char *spath, const char *dpath)
+{
+    int error;
+    int fd1;
+    int fd2;
+
+    fd1 = hc_open(&SrcHost, spath, O_RDONLY, 0);
+    fd2 = hc_open(&DstHost, dpath, O_RDONLY, 0);
+    error = -1;
+
+    if (fd1 >= 0 && fd2 >= 0) {
+	int n;
+	int x;
+
+	while ((n = hc_read(&SrcHost, fd1, IOBuf1, sizeof(IOBuf1))) > 0) {
+	    CountSourceReadBytes += n;
+	    x = hc_read(&DstHost, fd2, IOBuf2, sizeof(IOBuf2));
+	    if (x > 0)
+		    CountTargetReadBytes += x;
+	    if (x != n)
+		break;
+	    if (bcmp(IOBuf1, IOBuf2, n) != 0)
+		break;
+	}
+	if (n == 0)
+	    error = 0;
+    }
+    if (fd1 >= 0)
+	hc_close(&SrcHost, fd1);
+    if (fd2 >= 0)
+	hc_close(&DstHost, fd2);
+    return (error);
+}
+#if USE_PTHREADS
+
+static void *
+DoCopyThread(void *arg)
+{
+    copy_info_t cinfo = arg;
+    char *spath = cinfo->spath;
+    char *dpath = cinfo->dpath;
+ 
+    pthread_cond_init(&cinfo->cond, NULL);
+    pthread_mutex_lock(&MasterMutex);
+    cinfo->r += DoCopy(cinfo);
+    /* cinfo arguments invalid on return */
+    --cinfo->parent->children;
+    --CurParallel;
+    pthread_cond_signal(&cinfo->parent->cond);
+    pthread_mutex_unlock(&MasterMutex);
+    free(spath);
+    if (dpath)
+	free(dpath);
+    pthread_cond_destroy(&cinfo->cond);
+    free(cinfo);
+    return(NULL);
+}
+
+#endif
+
 int
-DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo)
+DoCopy(copy_info_t info)
 {
+    const char *spath = info->spath;
+    const char *dpath = info->dpath;
+    dev_t sdevNo = info->sdevNo;
+    dev_t ddevNo = info->ddevNo;
     struct stat st1;
     struct stat st2;
     int r, mres, fres, st2Valid;
     struct hlink *hln;
-    List list;
+    List *list = malloc(sizeof(List));
     u_int64_t size;
 
-    InitList(&list);
+    InitList(list);
     r = mres = fres = st2Valid = 0;
     size = 0;
     hln = NULL;
 
-    if (hc_lstat(&SrcHost, spath, &st1) != 0)
-	return(0);
-#ifdef SF_SNAPSHOT
-    /* skip snapshot files: we cannot handle files larger than file system */
-    if (st1.st_flags & SF_SNAPSHOT)
-        return(0);
-#endif
+    if (hc_lstat(&SrcHost, spath, &st1) != 0) {
+	r = 0;
+	goto done;
+    }
     st2.st_mode = 0;	/* in case lstat fails */
     st2.st_flags = 0;	/* in case lstat fails */
     if (dpath && hc_lstat(&DstHost, dpath, &st2) == 0)
 	st2Valid = 1;
 
     if (S_ISREG(st1.st_mode)) {
-	size = st1.st_blocks * 512;
-	if (st1.st_size % 512) 
-	    size += st1.st_size % 512 - 512;
+	size = st1.st_size;
     }
 
     /*
@@ -498,7 +605,8 @@
                     if (hln->nlinked == st1.st_nlink)
                         hltdelete(hln);
 		    CountSourceItems++;
-                    return 0;
+		    r = 0;
+		    goto done;
                 } else {
 		    /*
 		     * hard link is not correct, attempt to unlink it
@@ -538,7 +646,8 @@
 		    }
 		    CountSourceItems++;
 		    CountCopiedItems++;
-                    return 0;
+		    r = 0;
+		    goto done;
 		}
             }
         } else {
@@ -580,7 +689,8 @@
 		    else
 			logstd("%-32s nochange\n", (dpath ? dpath : spath));
 		}
-		return(0);
+		r = 0;
+		goto done;
 	    }
 #endif
 	} else {
@@ -590,11 +700,15 @@
 		st1.st_gid == st2.st_gid &&
 		st1.st_mtime == st2.st_mtime
 #ifndef NOMD5
-		&& (UseMD5Opt == 0 || (mres = md5_check(spath, dpath)) == 0)
+		&& (UseMD5Opt == 0 || !S_ISREG(st1.st_mode) ||
+		    (mres = md5_check(spath, dpath)) == 0)
 #endif
 #ifdef _ST_FSMID_PRESENT_
-		&& (UseFSMIDOpt == 0 || (fres = fsmid_check(st1.st_fsmid, dpath)) == 0)
+		&& (UseFSMIDOpt == 0 ||
+		    (fres = fsmid_check(st1.st_fsmid, dpath)) == 0)
 #endif
+		&& (ValidateOpt == 0 || !S_ISREG(st1.st_mode) ||
+		    validate_check(spath, dpath) == 0)
 	    ) {
                 if (hln)
                     hln->dino = st2.st_ino;
@@ -606,13 +720,15 @@
 #endif
 		    if (UseFSMIDOpt)
 			logstd("%-32s fsmid-nochange\n", (dpath ? dpath : spath));
+		    else if (ValidateOpt)
+			logstd("%-32s nochange (contents validated)\n", (dpath ? dpath : spath));
 		    else
 			logstd("%-32s nochange\n", (dpath ? dpath : spath));
 		}
 		CountSourceBytes += size;
 		CountSourceItems++;
-
-		return(0);
+		r = 0;
+		goto done;
 	    }
 	}
     }
@@ -622,7 +738,8 @@
 		(dpath ? dpath : spath)
 	    );
 	    ++r;		/* XXX */
-	    return(0);	/* continue with the cpdup anyway */
+	    r = 0;
+	    goto done; 		/* continue with the cpdup anyway */
 	}
 	if (QuietOpt == 0 || AskConfirmation) {
 	    logstd("%-32s WARNING: non-directory source will blow away\n"
@@ -703,15 +820,15 @@
 		} else {
 		    fpath = mprintf("%s/%s", spath, UseCpFile);
 		}
-		AddList(&list, strrchr(fpath, '/') + 1, 1);
+		AddList(list, strrchr(fpath, '/') + 1, 1);
 		if ((fi = fopen(fpath, "r")) != NULL) {
 		    while (fgets(buf, sizeof(buf), fi) != NULL) {
 			int l = strlen(buf);
-			CountReadBytes += l;
+			CountSourceReadBytes += l;
 			if (l && buf[l-1] == '\n')
 			    buf[--l] = 0;
 			if (buf[0] && buf[0] != '#')
-			    AddList(&list, buf, 1);
+			    AddList(list, buf, 1);
 		    }
 		    fclose(fi);
 		}
@@ -726,9 +843,9 @@
 	     * would otherwise overwrite the one we maintain on the target.
 	     */
 	    if (UseMD5Opt)
-		AddList(&list, MD5CacheFile, 1);
+		AddList(list, MD5CacheFile, 1);
 	    if (UseFSMIDOpt)
-		AddList(&list, FSMIDCacheFile, 1);
+		AddList(list, FSMIDCacheFile, 1);
 
 	    while (noLoop == 0 && (den = hc_readdir(&SrcHost, dir)) != NULL) {
 		/*
@@ -745,25 +862,54 @@
 		/*
 		 * ignore if on .cpignore list
 		 */
-		if (AddList(&list, den->d_name, 0) == 1) {
+		if (AddList(list, den->d_name, 0) == 1) {
 		    continue;
 		}
 		nspath = mprintf("%s/%s", spath, den->d_name);
 		if (dpath)
 		    ndpath = mprintf("%s/%s", dpath, den->d_name);
-		r += DoCopy(
-		    nspath,
-		    ndpath,
-		    sdevNo,
-		    ddevNo
-		);
-		free(nspath);
-		if (ndpath)
-		    free(ndpath);
+
+#if USE_PTHREADS
+		if (CurParallel < MaxParallel) {
+		    copy_info_t cinfo = malloc(sizeof(*cinfo));
+		    pthread_t dummy_thr = NULL;
+
+		    bzero(cinfo, sizeof(*cinfo));
+		    cinfo->spath = nspath;
+		    cinfo->dpath = ndpath;
+		    cinfo->sdevNo = sdevNo;
+		    cinfo->ddevNo = ddevNo;
+		    cinfo->parent = info;
+		    ++CurParallel;
+		    ++info->children;
+		    pthread_create(&dummy_thr, NULL, DoCopyThread, cinfo);
+		} else
+#endif
+		{
+		    info->spath = nspath;
+		    info->dpath = ndpath;
+		    info->sdevNo = sdevNo;
+		    info->ddevNo = ddevNo;
+		    r += DoCopy(info);
+		    free(nspath);
+		    if (ndpath)
+			free(ndpath);
+		}
 	    }
 
 	    hc_closedir(&SrcHost, dir);
 
+#if USE_PTHREADS
+	    /*
+	     * Wait for our children to finish
+	     */
+	    while (info->children) {
+		pthread_cond_wait(&info->cond, &MasterMutex);
+	    }
+	    r += info->r;
+	    info->r = 0;
+#endif
+
 	    /*
 	     * Remove files/directories from destination that do not appear
 	     * in the source.
@@ -782,7 +928,7 @@
 		     * If object does not exist in source or .cpignore
 		     * then recursively remove it.
 		     */
-		    if (AddList(&list, den->d_name, 3) == 3) {
+		    if (AddList(list, den->d_name, 3) == 3) {
 			char *ndpath;
 
 			ndpath = mprintf("%s/%s", dpath, den->d_name);
@@ -872,6 +1018,7 @@
 		if (st2Valid)
 			hc_remove(&DstHost, dpath);
 		if (hc_link(&DstHost, hpath, dpath) == 0) {
+			++CountLinkedItems;
 			if (VerboseOpt) {
 			    logstd("%-32s hardlinked(-H)\n",
 				   (dpath ? dpath : spath));
@@ -936,7 +1083,7 @@
 			    hc_chflags(&DstHost, dpath, st1.st_flags);
 #endif
 		    }
-		    CountReadBytes += size;
+		    CountSourceReadBytes += size;
 		    CountWriteBytes += size;
 		    CountSourceBytes += size;
 		    CountSourceItems++;
@@ -1015,8 +1162,9 @@
 		    logstd("%-32s nochange\n", (dpath ? dpath : spath));
 	    }
 	    CountSourceBytes += n1;
-	    CountReadBytes += n1;
-	    if (n2 > 0) CountReadBytes += n2;
+	    CountSourceReadBytes += n1;
+	    if (n2 > 0) 
+		CountTargetReadBytes += n2;
 	    CountSourceItems++;
 	} else {
 	    r = 1;
@@ -1060,7 +1208,9 @@
 	}
 	CountSourceItems++;
     }
-    ResetList(&list);
+done:
+    ResetList(list);
+    free(list);
     return (r);
 }
 
@@ -1341,6 +1491,8 @@
 	errno = e;
 #endif
     }
+    if (r == 0)
+	    ++CountLinkedItems;
     return(r);
 }
 
Index: do_cleanup
===================================================================
RCS file: /home/cvs/src/bin/cpdup/scripts/do_cleanup,v
retrieving revision 1.1
retrieving revision 1.2
diff -L bin/cpdup/scripts/do_cleanup -L bin/cpdup/scripts/do_cleanup -u -r1.1 -r1.2
--- bin/cpdup/scripts/do_cleanup
+++ bin/cpdup/scripts/do_cleanup
@@ -1,6 +1,6 @@
 #!/bin/csh
 #
-# $MidnightBSD$
+# $MIdnightBSD$
 # $DragonFly: src/bin/cpdup/scripts/do_cleanup,v 1.1 2006/09/16 21:57:10 dillon Exp $
 
 source params


More information about the Midnightbsd-cvs mailing list