🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.arch.storage
Article #369

tape2disk.c and disk2tape.c

#369
From: Ed Hamrick
Date: Thu, 19 May 1994 16:03
384 lines
11649 bytes
In article <2rfr8p$41t@netnews.upenn.edu>,
Kirsten Starcher <kirsten@ssc.sas.upenn.edu> wrote:
>We have a large number of IBM mainframe 9-track tapes, both labelled
>and unlabelled, that we would like to transcribe to our RS/6000s.  Does
>anyone know of a script or program that we could use to make a dumb
>copy of the tapes to online storage? (Preserving record length and
>block size.)

Attached is a program that transcribes a tape to a disk file
(tape2disk.c) and a program that does the reverse (disk2tape.c).

These programs were written to use the async I/O capability of
ConvexOS in order to get higher performance by overlapping disk
and tape I/O.  You'll have to change a few lines to adapt to AIX.

Regards,
Ed Hamrick

/*
 * tape2disk - Utility to copy a tape to a disk file (or pipe)
 *
 * This program copies an entire mag tape to a disk file (or pipe)
 * in a way that the tape can be completely reconstructed from the
 * disk file.
 *
 * The disk format is relatively simple.  For each tape record, a 4-byte
 * record length is stored, followed by the data.  For each end of file
 * mark, a zero is stored.  For each error reading the tape, a negative
 * one is stored.
 *
 * The companion program disk2tape rewrites a tape from the disk file.
 *
 * At the front of each disk file is a single 80-byte record containing
 * a comment for self-identification of the file.
 *
 * Usage:
 *
 * tape2disk tape disk comment
 *
 * if disk is "-", then output will be written to stdout
 *
 * Limitations:
 *
 * The maximum record length handled by this program is 256 KBytes.  This
 * is the maximum length supported by the 3490 and Metrum tape drives.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <errno.h>

/* Maximum data block size */

#define BUFFER_SIZE (256*1024)

/* Length of the initial label */

#define LABEL_SIZE 80

/* Number of sequential EOF's or errors that will terminate reading */

#define EOF_THRESHOLD 2

int       act;			/* Currently active I/O buffer */
int       eof;			/* Number of sequential EOF's or errors */
int       len;			/* Length of disk record */

int       fil;			/* File number (starts at 0) */
int       rec;			/* Record number (starts at 0) */
long long byt;			/* Total number of bytes */

struct {
  int  ret;			/* Value returned from read() */
  char buf[BUFFER_SIZE];	/* Data returned from read() */
} tap[2];

char hdr[LABEL_SIZE];		/* 80 byte file label */

long long itod() {
	struct timeval tp;
	gettimeofday(&tp,0);
	return (((long long)(tp.tv_sec) % 86400) * 1000 + 
		             tp.tv_usec / 1000);
}

main (int argc, char **argv) {

  int tape_fd;		/* Tape file descriptor */
  int disk_fd;		/* Disk file descriptor */

  long long t1,t2;	/* Start and end times */

  int first;		/* Flag for first time through loop */

  if (argc != 4) {
    fprintf(stderr,"Usage: tape2disk tape disk comment\n");
    exit(1);
  }

  memset(hdr, ' ', LABEL_SIZE);
  memcpy(hdr, argv[3], strlen(argv[3]));

  tape_fd = open(argv[1],O_RDONLY);

  if (!strcmp(argv[2],"-")) {
    disk_fd = fileno(stdout);
  }
  else {
    disk_fd = open(argv[2],O_RDWR|O_CREAT, 0755);
  }

  fcntl(tape_fd,F_SETFL,_FASIO);
  fcntl(disk_fd,F_SETFL,O_LARGEFILE);

  if (write(disk_fd, hdr, LABEL_SIZE) != LABEL_SIZE) {
    fprintf(stderr,"Error writing disk file\n");
    exit(1);
  }

  act = 0;
  eof = 0;

  fil = 0;
  rec = 0;
  byt = 0;

  first = 1;

  t1 = itod();
  do {
    if (read(tape_fd,tap[act].buf,BUFFER_SIZE) != BUFFER_SIZE) {
      fprintf(stderr,"Error starting tape I/O on file %d record %d errno %d\n",
              fil, rec, errno);
      exit(1);
    }
    if (!first) {				/* If not first time */
      if (tap[act^1].ret < 0) {			/*   if it got an error */
        len = 4;				/*     only store ret code */
        fprintf(stderr,"Error reading file %d record %d errno %d\n",
                fil, rec, errno);
      }
      else {					/*   if no error */
        len = 4 + tap[act^1].ret;		/*     store len + data */
        if (len == 4) {				/*   if an EOF mark */
          fil++;				/*     update file no */
          rec = 0;				/*     reset record no */
        }
        else {					/*   if a normal record */
          rec++;				/*     update record no */
          byt += len-4;				/*     update byte count */
        }
      }
      if (len == 4) {				/*   if not a data record */
        eof++;					/*     update eof count */
      }
      else {					/*   if a data record */
        eof = 0;				/*     clear eof count */
      }
      if (write(disk_fd, &tap[act^1].ret, len) != len) { /* write the disk */
        fprintf(stderr,"Error writing disk on file %d record %d errno %d\n",
                fil, rec, errno);
        exit(1);
      }
      if (eof >= EOF_THRESHOLD) {		/* All done with copy */
        t2 = itod();
        fprintf(stderr,"Copied %d files %lld bytes at %d Bytes/sec\n",
                fil, byt, (int) (byt/(t2-t1)));
        exit(0);
      }
    }
    else {
      first = 0;
    }
    tap[act].ret = asiostat(tape_fd);		/* Wait for tape I/O complete */
    act ^= 1;					/* Update active buffer num */
  } while (1);
}


/*
 * disk2tape - Utility to copy a disk file (or pipe) to a tape
 *
 * This program copies a disk file (or pipe) to an entire tape.  It
 * restores the tape image that was produced by the tape2disk program.
 *
 * The disk format is relatively simple.  For each tape record, a 4-byte
 * record length is stored, followed by the data.  For each end of file
 * mark, a zero is stored.  For each error reading the tape, a negative
 * one is stored.
 *
 * At the front of each disk file is a single 80-byte record containing
 * a comment for self-identification of the file.
 *
 * Usage:
 *
 * disk2tape disk tape
 *
 * if disk is "-", then input will come from stdin
 *
 * Limitations:
 *
 * The maximum record length handled by this program is 256 KBytes.  This
 * is the maximum length supported by the 3490 and Metrum tape drives.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/mtio.h>
#include <fcntl.h>
#include <errno.h>

/* Maximum data block size */

#define BUFFER_SIZE (256*1024)

/* Length of the initial label */

#define LABEL_SIZE 80

int       act;			/* Currently active I/O buffer */
int       len;			/* Length of disk record */

int       fil;			/* File number (starts at 0) */
int       rec;			/* Record number (starts at 0) */
long long byt;			/* Total number of bytes */

struct {
  int  len;			/* Length of buffer to write */
  char buf[BUFFER_SIZE];	/* Data to write */
} tap[2];

char hdr[LABEL_SIZE];		/* 80 byte file label */

long long itod() {
	struct timeval tp;
	gettimeofday(&tp,0);
	return (((long long)(tp.tv_sec) % 86400) * 1000 + 
		             tp.tv_usec / 1000);
}
/*
 * Note that a special read routine is needed for reading from
 * a pipe because in general, a read can return successfully with
 * fewer bytes read than requested.  The read routine must be
 * synchronous for this reason.
 */

static int pipe_read(int pipe_fd, char *pipe_buf, int pipe_siz) {
  int tot = 0;
  int siz;
  while (tot < pipe_siz) {
    siz = read(pipe_fd, pipe_buf+tot, pipe_siz-tot);
    if (siz <  0) return siz;
    if (siz == 0) return tot;
    tot += siz;
  }
  return tot;
}


main (int argc, char **argv) {

  int tape_fd;		/* Tape file descriptor */
  int disk_fd;		/* Disk file descriptor */

  int i, j;

  long long t1,t2;	/* Start and end times */

  struct mtop weof = {MTWEOF, 1};

  if (argc != 3) {
    fprintf(stderr,"Usage: disk2tape disk tape\n");
    exit(1);
  }

  memset(hdr, ' ', LABEL_SIZE);

  if (!strcmp(argv[1],"-")) {
    disk_fd = fileno(stdin);
  }
  else {
    disk_fd = open(argv[1],O_RDONLY);
  }

  tape_fd = open(argv[2],O_RDWR);

  fcntl(tape_fd,F_SETFL,_FASIO);
  fcntl(disk_fd,F_SETFL,O_LARGEFILE);

  if (pipe_read(disk_fd, hdr, LABEL_SIZE) != LABEL_SIZE) {
    fprintf(stderr,"Error reading comment from disk file\n");
    exit(1);
  }
  for (i=79; i>=0; i--) {
    if (hdr[i] != ' ') break;
  }
  if (i > 0) {
    hdr[i+1] = 0;
    fprintf(stderr,"Comment - %s\n",hdr);
  }

  act = 0;

  fil = 0;
  rec = 0;
  byt = 0;

  tap[0].len = 0;
  tap[1].len = 0;

  t1 = itod();
  do {

    /* Read info about what should be written to tape */
    i = pipe_read(disk_fd, (char *) &tap[act].len, sizeof(int));
    if (i < 0) {
      fprintf(stderr,"Error reading disk at file %d record %d errno %d\n",
              fil, rec, errno);
      exit(1);
    }
    if (i == 0) {
      if (tap[act^1].len > 0) {
        if (asiostat(tape_fd) != tap[act].len) {
          fprintf(stderr,"Error writing tape at file %d record %d errno %d\n",
                  fil, rec, errno);
          exit(1);
        }
      }
      t2 = itod();
      fprintf(stderr,"Copied %d files %lld bytes at %d Bytes/sec\n",
              fil, byt, (int) (byt*1000/(t2-t1)));
      exit(0);
    }
    if (tap[act].len > 0) {
      if (pipe_read(disk_fd, tap[act].buf, tap[act].len) != tap[act].len) {
        fprintf(stderr,"Error reading disk at file %d record %d errno %d\n",
                fil, rec, errno);
        exit(1);
      }
    }

    /* Wait for completion of last write if necessary */
    if (tap[act^1].len > 0) {
      if (asiostat(tape_fd) != tap[act^1].len     ) {
        fprintf(stderr,"Error writing tape at file %d record %d errno %d\n",
                fil, rec, errno);
        exit(1);
      }
    }


    /* Write an eof or start the data write if necessary */
    if (tap[act].len < 0) {
        fprintf(stderr,"Unable to recreate error at file %d record %d\n",
                fil, rec);
    }
    else if (tap[act].len == 0) {
      if (ioctl(tape_fd, MTIOCTOP, &weof) < 0) {
        fprintf(stderr,"Error writing eof at file %d record %d errno %d\n",
                fil, rec, errno);
        exit(1);
      }
      fil++;
      rec = 0;
    }
    else {
      if (write(tape_fd,tap[act].buf,tap[act].len) != tap[act].len) {
        fprintf(stderr,"Error writing tape at file %d record %d errno %d\n",
                fil, rec, errno);
        exit(1);
      }
      rec++;
      byt += tap[act].len;
    }

    act ^= 1;					/* Update active buffer num */
  } while (1);
}
Technical Details
Message-ID: <2rg2kg$k4n@convex.convex.com>
Path: arc1-ca.pugleaf.net!bga.com!news.sprintlink.net!get.hooked.net!decwrl!hookup!yeshua.marcam.com!MathWorks.Com!europa.eng.gtefsd.com!howland.reston.ans.net!darwin.sura.net!convex!hamrick
References: <2rfr8p$41t@netnews.upenn.edu>

Show Full Headers
Complete Article Headers
X-Google-Language: ENGLISH,ASCII-7-bit
X-Google-Thread: f91da,3c2d8d55e3619294
X-Google-Attributes: gidf91da,public
X-Google-Thread: 10e1c8,3c2d8d55e3619294
X-Google-Attributes: gid10e1c8,public
X-Google-Thread: 115614,3c2d8d55e3619294
X-Google-Attributes: gid115614,public
X-Google-Thread: 10d52b,769441441d1ea0e9,start
X-Google-Attributes: gid10d52b,public
X-Google-ArrivalTime: 1994-05-19 09:17:56 PST
Path: arc1-ca.pugleaf.net!bga.com!news.sprintlink.net!get.hooked.net!decwrl!hookup!yeshua.marcam.com!MathWorks.Com!europa.eng.gtefsd.com!howland.reston.ans.net!darwin.sura.net!convex!hamrick
From: hamrick@convex.com (Ed Hamrick)
Newsgroups: comp.arch.storage,comp.unix.aix,bit.listserv.aix-l,comp.sys.convex
Subject: tape2disk.c and disk2tape.c
Date: 19 May 1994 16:03:28 GMT
Organization: CONVEX Computer Corp, Richardson, TX USA
Lines: 384
Message-ID: <2rg2kg$k4n@convex.convex.com>
References: <2rfr8p$41t@netnews.upenn.edu>
NNTP-Posting-Host: convex1.convex.com