/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:rvdcopy.c 12.0$ */
/* $ACIS:rvdcopy.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ibm/rvd/misc/RCS/rvdcopy.c,v $ */

#ifndef lint
static char *rcsid = "$Header:rvdcopy.c 12.0$";
#endif


#ifndef lint
static char *rcsid_rvdcopy_c = "$Header:rvdcopy.c 12.0$";
#endif lint

/*  Copyright 1986 by the Massachusetts Institute of Technology  */
/*  See permission and disclaimer notice in file "notice.h"  */
#include	"notice.h"


/* 
 *  Copy contents of one RVD disk pack to another.  
 * 				<J. H. Saltzer, 3/26/86> 
 *	ported to UNIX by
 *				 PhilipP, 6 May 1986 (I think)
 */

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <machineio/vdreg.h>

#define	MIN(a,b)	((a) <= (b) ? (a) : (b))
#define	DRIVE(raw)	(raw ? "/dev/rvd%da" : "/dev/vd%da")

#define MAXBURST	32	/* chosen to match rvd client burst size */
#define	BURST		8
#define	BSIZE		512
#define	PSIZE		1024
#define	BFACTOR		(PSIZE/BSIZE)

#define INTERVAL	(100)		/* progress report interval */

extern	int	errno;
int	vdcntrl;
char	*myname = "rvdcopy";
char	*vd_control_name = "/dev/rvdcontrol";

static	int	wflag = 1;

main(argc, argv)
int	argc;
char	**argv;
{
int	d1, d2;		/* drive numbers, zero based */
off_t	i, j;		/* temporaries */
int	burstsize;	/* read this many blocks */
off_t	start;		/* first block to copy. */
long	nblocks = -1;	/* number of blocks to copy */
int	rderrors = 0;	/* number of errors reading */
int	wrerrors = 0;	/* number of errors writing */
struct	vd_longstat	astat, bstat;	/* before and after stats. */
struct	vd_longstat	*stats;
struct	vd_long_dev	*drives;
int	fd1, fd2;
char	*t, *getenv(), c = 0;
int	burst = BURST;
int	raw = 1;
char	vd1[VD_NAME_LEN];
char	vd2[VD_NAME_LEN];
char	*buff;	/* copy through here */


	if((t = getenv("BURST")) != NULL && sscanf(t, "%d", &burst) != 1) {
		fprintf(stderr, "%s: illegal burstsize %s\n", argv[0], t);
		exit(-1);
	}
	burst *= BFACTOR;

	while(argc > 1 && argv[1][0] == '-') {
	top:
		++argv[1];
		switch(argv[1][0]) {
		case 'b':	/* block device */
			raw = 0;
			goto top;
		case 'c':
			raw = 1;
			goto top;
		case 'w':
			wflag = 0;
			goto top;
		case '\0':
			goto bot;
		default:
			fprintf(stderr, "%s: illegal switch %c\n",
			  myname, argv[1][0]);
			goto top;
		}
	bot:
		++argv;
		--argc;
	}

	/* First, check over the arguments */

	if (argc < 3 || 5 < argc) {
		fprintf(stderr, "usage: %s from_drive_num to_drive_num [ blockcount [ startblock ]]\n",
		  myname);
		exit(1);
	}

	if(sscanf(argv[1], "%d", &d1) != 1 || d1 < 0 || 9 < d1) {
		fprintf(stderr, "%s: illegal drive: %s\n", myname, argv[1]);
		exit(1);
	}
	if(sscanf(argv[2], "%d", &d2) != 1 || d2 < 0 || 9 < d2) {
		fprintf(stderr, "%s: illegal drive: %s\n", myname, argv[2]);
		exit(1);
	}
	if(d1 == d2) {
		fprintf(stderr, "%s: drives are identical!\n", myname);
		exit(1);
	}

	if(argc >= 4) {
		if(sscanf(argv[3], "%d", &nblocks) != 1) {
			fprintf(stderr, "%s: illegal block count: %s\n",
			  myname, argv[3]);
			exit(2);
		}
		if(argc == 5) {
			if(sscanf(argv[4], "%d", &start) != 1) {
				fprintf(stderr, "%s: illegal starting block: %s\n",
				  myname, argv[4]);
				exit(3);
			}
		} else
			start = 0;
	}

	/*
	 * should do the open before the vdstats because we
	 * don't want to count packets used in opening the pack.
	 */

	sprintf(vd1, DRIVE(raw), d1);
	sprintf(vd2, DRIVE(raw), d2);
	printf("using %s\n", vd1);
	printf("and %s\n", vd2);

	if ((fd1 = open(vd1, O_RDONLY)) < 0) {
		perror(vd1);
		exit(5);
	}
	if ((fd2 = open(vd2, O_WRONLY)) < 0) {
		perror(vd2);
		exit(6);
	}

	/* Arguments are clean.  Save statistics for later comparison.  */

	if((vdcntrl = open(vd_control_name, O_RDONLY, 0)) < 0) {
		perror(vd_control_name);
		exit(1);
	}
	stats = &astat;
	if(ioctl(vdcntrl, VDIOCGETSTAT, &stats)) {
		perror(vd_control_name);
		exit(1);
	}

	drives = (struct vd_long_dev *)malloc(astat.num_drives * sizeof(struct vd_long_dev));
	if(drives == 0) {
		fprintf(stderr, "%s: Could not get memory for drive structs\n",
			myname);
		exit(1);
	}
	if(ioctl(vdcntrl, VDIOCGETDRIVE, &drives)) {
		perror(myname);
		exit(1);
	}
	/* Now do the copy.  */

	if(nblocks == -1) {
		nblocks = MIN(drives[d1].vd_device.nblocks, drives[d2].vd_device.nblocks);

		if(drives[d1].vd_device.nblocks != drives[d2].vd_device.nblocks)
			fprintf(stderr,
		"%s: packs of unequal lengths (%d, %d). assuming smaller.\n",
				myname, drives[d1].vd_device.nblocks, drives[d2].vd_device.nblocks);
	}

	free(drives);
	drives = 0;

	if((buff = (char *)malloc(burst * PSIZE)) == 0) {
		fprintf(stderr, "%s: Could not get memory for data buffer\n",
			myname);
		exit(1);
	}

	for (i = 0; i < nblocks; i += burstsize) {
		if (i % INTERVAL < burst) {
			printf("\rdone: %6u", i);
			fflush(stdout);
			c = '\n';
		}
		burstsize = burst;
		if ((burstsize + i) > nblocks)
			burstsize = nblocks - i;

		if (breadn(fd1, i + start, burstsize, buff)) {
			/* error: retry one at a
			 * time, to locate bad
			 * block */

			for (j = 0; j < burstsize; j++)
				if(breadn(fd1, i+j+start, 1, buff+j*BSIZE)) {
					if(c) {
						putchar(c);
						c = 0;
					}
					printf("error %d reading drive %d at block %u\n",
					  errno, d1, i + j + start);
					rderrors++;
				}
		}

		if (bwriten(fd2, i+start, burstsize, buff)) {
			/* error: retry one at a
			 * time, to locate bad
			 * block */

			for (j = 0; j < burstsize; j++)
				if(bwriten(fd2, i+j+start, 1, buff+j*BSIZE)) {
					if(c) {
						putchar(c);
						c = 0;
					}
					printf("error %d writing drive %d at block %u\n",
					  errno, d2, i + j + start);
					wrerrors++;
				}
		}
	}

	printf("\n%s: %u blocks of pack on drive %d", myname, nblocks, d1);
	printf(" copied to drive %d\n", d2);

	if ((rderrors + wrerrors) != 0) {
		printf("%s:  %u read errors, %u write errors\n",
		  myname, rderrors, wrerrors);
		printf("Disk pack on drive %d may be unusable.\n", d2);
	}
	/* Get ending statistics, subtract starting statistics, and display. */

	stats = &bstat;
	if(ioctl(vdcntrl, VDIOCGETSTAT, &stats)) {
		perror(myname);
		exit(1);
	}

	printf("Packets sent: %u; received: %u, includes %u error packets\n",
		bstat.vdstat.pkts_sent - astat.vdstat.pkts_sent,
		bstat.vdstat.pkts_rcvd - astat.vdstat.pkts_rcvd,
		bstat.vdstat.err_rcv - astat.vdstat.err_rcv);
	printf("Blocks requested: %u; written %u; Retransmitts %u\n",
		bstat.vdstat.blk_rqs - astat.vdstat.blk_rqs,
		bstat.vdstat.blk_wrt - astat.vdstat.blk_wrt,
		bstat.vdstat.rxmts - astat.vdstat.rxmts);

	printf("bad checksums: %u; timeouts: %u; bad nonces: %u\n",
		bstat.vdstat.bad_cksum - astat.vdstat.bad_cksum,
		bstat.vdstat.timeout - astat.vdstat.timeout,
		bstat.vdstat.bad_nonce - astat.vdstat.bad_nonce);

}

/*
 * easier to fake the functions than include the code in-line, so...
 * here it is:  MS-DOS COMPATIBILITY ROUTINES!!!   AAAARRRRGGGGHHHHH!!!!!!!
 */

breadn(d, b, c, p)
int	d, c;
off_t	b;
char	*p;
{
	b *= BSIZE;
	c *= BSIZE;

	if(lseek(d, b, L_SET) < 0) {
		perror("lseek(breadn)");
		abort();
	}

	if(read(d, p, c) != c) {
		return(-1);
	}

	return(0);
}

bwriten(d, b, c, p)
int	d, c;
off_t	b;
char	*p;
{

	if(! wflag)
		return(0);

	b *= BSIZE;
	c *= BSIZE;

	if(lseek(d, b, L_SET) < 0) {
		perror("lseek(bwriten)");
		abort();
	}

	if(write(d, p, c) != c) {
		return(-1);
	}

	return(0);
}
