1
0
mirror of https://github.com/brouhaha/tapeutils.git synced 2026-01-11 23:53:18 +00:00
This commit is contained in:
Lars Brinkhoff 2016-10-27 11:03:18 +02:00
parent 86e5f36943
commit a9d00fca34
9 changed files with 2131 additions and 66 deletions

View File

@ -20,11 +20,11 @@
# options
# -----------------------------------------------------------------------------
CFLAGS = -O2
LDFLAGS =
# CFLAGS = -O2 -Wall
# LDFLAGS =
# CFLAGS = -g
# LDFLAGS = -g
CFLAGS = -g -Wall
LDFLAGS = -g
# -----------------------------------------------------------------------------
# You shouldn't have to change anything below this point, but if you do please
@ -32,13 +32,13 @@ LDFLAGS =
# -----------------------------------------------------------------------------
PACKAGE = tapeutils
VERSION = 0.4
VERSION = 0.6
DSTNAME = $(PACKAGE)-$(VERSION)
PROGRAMS = tapecopy tapedump
PROGRAMS = tapecopy tapedump t10backup read20
HEADERS = tapeio.h
SOURCES = tapeio.c tapecopy.c tapedump.c
HEADERS = tapeio.h t10backup.h dumper.h
SOURCES = tapeio.c tapecopy.c tapedump.c t10backup.c read20.c
MISC = COPYING
DISTFILES = $(MISC) Makefile $(HEADERS) $(SOURCES)
@ -60,6 +60,10 @@ tapecopy: tapecopy.o tapeio.o
tapedump: tapedump.o tapeio.o
t10backup: t10backup.o tapeio.o
read20: read20.o tapeio.o
include $(SOURCES:.c=.d)

133
dumper.h Normal file
View File

@ -0,0 +1,133 @@
/* 5 bytes per 36-bit word */
/* 518 word logical blocks */
#define TAPEBLK 518*5*15
/* Checksum is first word */
#define WdoffChecksum 0
#define BtoffChecksum 0
#define BtlenChecksum 36
/* Page access bits is second word */
#define WdoffAccess 1
#define BtoffAccess 0
#define BtlenAccess 36
/* SCD, first 3 bits in next word */
#define WdoffSCD 2
#define BtoffSCD 0
#define BtlenSCD 3
/* Number of saveset on tape */
#define WdoffSaveSetNum 2
#define BtoffSaveSetNum 3
#define BtlenSaveSetNum 15
/* Tape number of dump */
#define WdoffTapeNum 2
#define BtoffTapeNum 18
#define BtlenTapeNum 18
/* F1, F2 Flag bits */
#define WdoffF1F2 3
#define BtoffF1F2 0
#define BtlenF1F2 2
/* File Number in Set (new format only) */
#define WdoffFileNum 3
#define BtoffFileNum 2
#define BtlenFileNum 16
/* Page Number in file */
#define WdoffPageNum 3
#define BtoffPageNum 18
#define BtlenPageNum 18
/* Record type (2's complement) */
#define WdoffRectype 4
#define BtoffRectype 0
#define BtlenRectype 36
/* Record sequence number */
#define WdoffRecseq 5
#define BtoffRecseq 0
#define BtlenRecseq 36
/* SCD Values */
#define SCDNormal 0
#define SCDCollection 1
#define SCDArchive 2
#define SCDMigration 3
/* F1, F2 Values */
#define F1F2Old 0
#define F1F2OldContinue 3
#define F1F2New 1
#define F1F2NewContinue 2
/* Record type values */
#define RectypeData 0
#define RectypeTphd 1
#define RectypeFlhd 2
#define RectypeFltr 3
#define RectypeTptr 4
#define RectypeUsr 5
#define RectypeCtph 6
#define RectypeFill 7
char *rectypes[] = {
"DATA",
"ISSH",
"FLHD",
"FLTR",
"TPTR",
"UDIR",
"CSSH",
"FILL",
};
#define BtoffWord 0
#define BtlenWord 36
#define WdoffSSDate 8 /* Saveset date offset (type 1, 6) */
#define WdoffSSName 9 /* Saveset name offset (type 1, 6) */
#define WdoffFLName 6 /* Filename offset (type 2) */
#define WdoffFDB 134 /* FDB offset (type 2) */
#define WdoffFDB_CTL 01+WdoffFDB /* Control word .FBCTL */
#define BtoffFDB_Arc 11 /* archived */
#define BtlenFDB_Arc 1
#define BtoffFDB_Inv 12 /* invisible */
#define BtlenFDB_Inv 1
#define BtoffFDB_Off 13 /* offline */
#define BtlenFDB_Off 1
#define WdoffFDB_PRT 04+WdoffFDB /* protection */
#define BtoffFDB_PRT 18
#define BtlenFDB_PRT 18
#define WdoffFDB_BSZ 011+WdoffFDB /* Number of bits per byte */
#define BtoffFDB_BSZ 6
#define BtlenFDB_BSZ 6
#define WdoffFDB_PGC 011+WdoffFDB /* Number of pages in the file */
#define BtoffFDB_PGC 18
#define BtlenFDB_PGC 18
#define WdoffFDB_Size 012+WdoffFDB /* Number of bytes in the file */
#define BtoffFDB_Size 0
#define BtlenFDB_Size 36
#define WdoffFDB_Wrt 014+WdoffFDB /* Date of last write to file */
#define WdoffFDB_Ref 015+WdoffFDB /* read time */
#define WdoffFDB_PGC_A 022+WdoffFDB /* Pagecount before archive */
#define WdoffFDB_TP1 033+WdoffFDB /* Tape ID for archive run 1 */
#define WdoffFDB_SS1 034+WdoffFDB /* Saveset # for archive run 1 */
#define BtoffFDB_SS 0
#define BtlenFDB_SS 18
#define WdoffFDB_TF1 034+WdoffFDB /* Tape file # for archive run 1 */
#define BtoffFDB_TF 18
#define BtlenFDB_TF 18
#define WdoffFDB_TP2 035+WdoffFDB /* Tape ID for archive run 2 */
#define WdoffFDB_SS2 036+WdoffFDB /* Saveset # for archive run 2 */
#define WdoffFDB_TF2 036+WdoffFDB /* Tape file # for archive run 2 */

892
read20.c Normal file
View File

@ -0,0 +1,892 @@
/*
* Program to read Tops-20 Dumper format tapes
*
* Jim Guyton, Rand Corporation
* Original 10/20/82
* jdg: -n added 6/11/83
* jdg: can now extract 8-bit-byte files 2/9/86
*
* Lot of mods by Jay Lepreau, Univ of Utah, 1-2/87.
* See the RCS log for details.
*
* Modified by Eric Smith <eric@brouhaha.com>, 4-DEC-2000, to use
* tapeio library, in order to use tape image files. Also changed
* to use environment variable TAPE if set, otherwise default to /dev/nst0.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <utime.h>
#include <fcntl.h>
#include <unistd.h>
#define _REGEX_RE_COMP
#include <regex.h>
#include "dumper.h"
#include "tapeio.h"
#define LOGFILE "Logfile" /* logfile should be changeable */
tape_handle_t tape_handle;
char tapeblocka[TAPEBLK]; /* One logical record from tape */
FILE *fpFile; /* Output file handle on extracts */
int debug = 0;
int textflg = 0; /* Non-zero if retr binary files as text */
int numflg = 0; /* Non-zero if using numeric filenames */
int keepcr = 0; /* Keep CR's in CRLF pairs in text files */
int dodir = 0; /* directory listing */
int xflg = 0; /* extract */
int verbose = 0;
int genflg; /* keep generation number */
int nselect; /* number of files still to be selected by number */
int doallflag; /* act on all files cause no args given */
int number; /* Current output file "number" */
#define TAPE "/dev/nst0" /* Default input tape */
int bytesize; /* Number of bits/byte in current file */
long numbytes; /* Number of bytes in current file */
int pgcount; /* Number of twenex pages in file */
long pageno, tapeno, ssno, filenum;
unsigned tprot; /* Tops-20 protection */
char *timeptr;
struct utimbuf timep;
int offline, archived, invisible;
int apgcount, tp1, tp2, ss1, ss2, tf1, tf2;
char topsname[130];
char sunixname[300];
struct want {
unsigned short ssnum;
unsigned short fnum;
} want[10000]; /* limited by 20000 char arglist */
int cursswant;
char **patterns = 0; /* Filename match patterns */
int numpats = 0; /* Number of patterns */
char *expression = 0;
char *re_comp_error; /* Error message from re_comp() */
extern char *re_comp();
/*
read20 [-f tapefile] [-t] [-c] [-T] [-n number] pattern
no tapefile ==> /dev/rmt8
-t == directory listing
-n == use numeric filenames in extracts, number is 1st name
-T == pretend 36 bit files are 7-bit ascii
-c == keep CR's in CRLF pairs.
-g == keep generation numbers
*/
void punt (int prterrno, char *fmt, ...)
{
extern int errno, sys_nerr;
va_list ap;
va_start (ap, fmt);
vfprintf(stderr, fmt, ap);
va_end (ap);
if (prterrno) {
fprintf(stderr, ": ");
if (errno >= 0 && errno < sys_nerr)
perror("");
else
fprintf(stderr, "ERRNO = %d\n", errno);
}
else
fprintf(stderr, "\n");
exit(1);
}
/* fold -- perform case folding
*
* Usage: p = fold (out,in,whichway);
* p = foldup (out,in);
* p = folddown (out,in);
* char *p,*in,*out;
* enum {FOLDUP, FOLDDOWN} whichway;
*
* Fold performs case-folding, moving string "in" to
* "out" and folding one case to another en route.
* Folding may be upper-to-lower case (folddown) or
* lower-to-upper case.
* Foldup folds to upper case; folddown folds to lower case.
* The same string may be specified as both "in" and "out".
* The address of "out" is returned for convenience.
*
* HISTORY
* 20-Nov-79 Steven Shafer (sas) at Carnegie-Mellon University
* Rewritten for VAX; now uses enumerated type for fold(). The
* foldup() and folddown() routines are new.
*
*/
typedef enum {
FOLDUP, FOLDDOWN}
FOLDMODE;
char *fold (char *out, char *in, FOLDMODE whichway)
{
register char *i,*o;
register char lower;
char upper;
int delta;
switch (whichway)
{
case FOLDUP:
lower = 'a'; /* lower bound of range to change */
upper = 'z'; /* upper bound of range */
delta = 'A' - 'a'; /* amount of change */
break;
case FOLDDOWN:
default:
lower = 'A';
upper = 'Z';
delta = 'a' - 'A';
}
i = in;
o = out;
do {
if (*i >= lower && *i <= upper) *o++ = *i++ + delta;
else *o++ = *i++;
}
while (*i);
*o = '\0';
return (out);
}
char *foldup (char *out, char *in)
{
return (fold(out,in,FOLDUP));
}
char *folddown (char *out, char *in)
{
return (fold(out,in,FOLDDOWN));
}
int masks[32] = /* bitmasks for different length fields */
{ 0x00000001, 0x00000003, 0x00000007,
0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff,
0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
0xffffffff
};
long getfield (char *block, /* Tape block record */
int wordoff, /* 36-bit word offset */
int bitoff, /* Bit offset of field (from msb) */
int bitlen) /* Bit length of field */
{
register char *p; /* Used to point into record */
register long w32; /* First 32 bits of the 36 bit word */
int w4; /* Last 4 bits of the 36 bit word */
long w = 0; /* the word to return */
/* First, the "illegal" kludge */
if (bitoff == 0 && bitlen == 36) {
bitoff = 4;
bitlen = 32;
}
if (bitlen > 32)
punt(0, "Can't get that large a field = %d!", bitlen);
/* A PDP-10 (or 20) 36-bit word is laid out with the first 32 bits
as the first 4 bytes and the last 4 bits are the low order 4 bits
of the 5th byte. The high 4 bits of that byte should be zero */
p = block + (5*wordoff); /* Get ptr to word of interest */
w32 = *p++ & 0377; /* First byte */
w32 = (w32 << 8) | (*p++ & 0377); /* 2nd */
w32 = (w32 << 8) | (*p++ & 0377); /* 3rd */
w32 = (w32 << 8) | (*p++ & 0377); /* 4th */
w4 = *p; /* 5th */
if (w4 > 017)
punt(0, "Not a PDP-10 tape! w4 = octal %o", w4);
/* Get the field right justified in the word "w".
There are three cases that I have to handle:
[1] field is contained in w32
[2] field crosses w32 and w4
[3] field is contained in w4
*/
if (bitoff+bitlen <= 32) /* [1] field is contained in w32 */
{
w = w32 >> (32 - (bitoff+bitlen));
}
else if (bitoff <= 32) /* [2] field crosses boundary */
{
w = (w32 << (bitoff+bitlen-32))
| (w4 >> (36 - (bitoff+bitlen)));
}
else /* [3] field is contained in w4 */
{
w = w4 >> (36 - (bitoff+bitlen));
}
w = w & masks[bitlen-1]; /* Trim to proper size */
return(w);
}
int lastc = 0;
/*
* Unpack into buffer 's' the 7 bit string stored in 'block' and
* append a null char. Optionally strip CR's from CRLF pairs. 'max'
* is the max number of 7-bit chars to unpack from 'block', not the
* max to put into 's' (that's important!). This only works if
* getstring() is always called with 'max' mod 5 == 0, except for the
* last call on "contiguous" blocks.
* Returns number of chars stored in output buffer.
*/
int getstring (char *block, /* Tape block */
char *s /* Destination string buffer */,
int wordoff, /* 36-bit offset from start of tape block */
int max) /* Max number of characters to xfer into s */
{
register int i; /* Counter for five characters per word */
int ct = 0; /* Number of characters loaded so far */
char *orig = s; /* Save for debugging */
int c;
while (ct < max)
{
for (i = 0; i < 5; i++)
{
c = getfield(block, wordoff, i*7, 7);
if (lastc == '\r' && c != '\n')
*s++ = '\r';
if (c != '\r' || keepcr)
*s++ = c;
if (!keepcr)
lastc = c;
if ((ct + i + 1) == max)
return (s - orig);
}
wordoff++;
ct += 5;
}
printf("Fall thru in getfield\n");
fflush(stdout);
*s = '\0';
return (s - orig);
}
/*
* pendstring - return any character pending output after
* last call to getstring(). Also zeros `lastc'.
* Can only return '\r' or 0 for none.
*/
int pendstring (void)
{
int olastc = lastc;
lastc = 0;
return (olastc == '\r') ? '\r' : 0;
}
/* getbytes: like getstring, but ...
1) uses 8 bit bytes
2) doesn't stop on a zero
*/
void getbytes (char *block, /* Tape block */
char *s, /* Destination string buffer */
int wordoff, /* 36-bit offset from start of tape block */
int max) /* Max number of characters to xfer into s */
{
register int i; /* Counter for five characters per word */
int ct = 0; /* Number of characters loaded so far */
/* char *orig = s; */ /* Save for debugging */
while (ct < max)
{
for (i = 0; i < 4; i++)
{
*s = getfield(block, wordoff, i*8, 8);
/* if (*s == 0) return; */
s++;
}
wordoff++;
ct += 4;
}
/** punt(0, "String greater than %d characters.", max); **/
}
#define SecPerTick (24.*60.*60.)/0777777
#define DayBaseDelta 0117213 /* Unix day 0 in Tenex format */
/*
* This screws up on some of the atime's we see, including, yielding, e.g.
* Fri Dec 23 23:28:16 1994
* Fri Dec 23 23:28:16 1994
* Tue Jan 13 07:57:03 1987
*/
long unixtime(char *block, int wordoff)
{
long int t, s;
t = getfield(block, wordoff, 0, 18); /* First half is day */
t -= DayBaseDelta; /* Switch to unix base */
/* Now has # days since */
/* Jan 1, 1970 */
s = getfield(block, wordoff, 18, 18); /* 2nd half is fraction day */
s = s * SecPerTick; /* Turn into seconds */
s += t*24*60*60; /* Add day base */
return(s);
}
char *unixname (char *name)
{
static FILE *log = NULL;
register char *t, *p;
static char lastdir[64];
struct stat stb;
int mask;
register int newdir = 0;
if (numflg) { /* If numeric filenames */
if (log == NULL) log = fopen(LOGFILE, "a");
fprintf(log, "%d is %s\n", number, name);
sprintf(sunixname, "%d", number++);
return(sunixname);
}
strcpy(sunixname, index(name, '<') + 1); /* trim off device */
t = rindex(sunixname, '>'); /* find end of directory */
*t = '.';
if (strncmp(lastdir, sunixname, t - sunixname)) {/* maybe new dir */
strncpy(lastdir, sunixname, t - sunixname); /* remember it */
newdir = 1;
}
for (p = sunixname; p <= t; p++)
if (*p == '.') {
if (newdir) {
*p = '\0'; /* temporarily null it off */
if (stat(sunixname, &stb) < 0) {
mask = umask(2);
if (mkdir(sunixname, 0777) < 0)
punt(1, "mkdir %s failed", sunixname);
umask(mask);
}
}
*p = '/';
}
if (!genflg) {
t = rindex(sunixname, '.'); /* find last . */
*t = 0; /* zap it out */
}
return(sunixname);
}
void doDatablock (char *block)
{
/* max is 5 bytes per word */
static char buf[(512*5)+1]; /* A page of characters */
int ct;
int maxperblock;
int nout;
if (debug > 10)
printf("*");
if (fpFile == NULL)
return;
switch (bytesize) { /* only handle 7 and 8 bit bytes */
case 7: maxperblock = 512*5; break;
case 8: maxperblock = 512*4; break;
default: return;
}
if (numbytes > maxperblock)
ct = maxperblock;
else
ct = numbytes;
if (bytesize == 7) {
nout = getstring(block, buf, 6, ct);
fwrite(buf, 1, nout, fpFile);
}
else { /* if not 7, then 8bit */
getbytes(block, buf, 6, ct);
fwrite(buf, 1, ct, fpFile);
}
if (ferror(fpFile))
punt(1, "Error writing %s", sunixname);
numbytes -= ct;
}
void doSaveset (char *block, int contflag)
{
static char name[102];
static char ss[2];
long t;
if (debug > 10) printf("\nSaveset header:");
tapeno = getfield(block, WdoffTapeNum, BtoffTapeNum, BtlenTapeNum);
ssno = getfield(block, WdoffSaveSetNum, BtoffSaveSetNum,
BtlenSaveSetNum);
getstring(block, name, WdoffSSName, sizeof(name));
ss[0] = pendstring(); /* superfluous */
(void) strcat(name, ss);
t = unixtime(block, WdoffSSDate);
if (dodir || verbose)
printf("%sSaveset '%s' %s\n", contflag ? "Continued " : "",
name, ctime(&t));
}
/* Return 1 if topsname matches any of the "extraction" strings. */
int patternmatch (void)
{
register int i;
for (i = 0; i < numpats; i++)
if (strstr(topsname, patterns[i]))
return (1);
return (0);
}
/* Return 1 if topsname matches the regular expression. */
int expmatch (void)
{
register int match;
if (expression) {
if ((match = re_exec(topsname)) == -1)
punt(0, "re_exec: internal error on %s", topsname);
else
return (match);
}
return (0);
}
/* Return 1 if current file number matches one selected by arg line. */
int fmatch (void)
{
static int widx;
while (want[widx].ssnum < ssno)
widx++;
if (want[widx].ssnum > ssno)
return 0;
while (want[widx].fnum < filenum)
widx++;
if (want[widx].fnum > filenum)
return 0;
return 1;
}
/*
* Sets a bunch of global variables to info from the fdb.
* For some reason the archive tape info is garbage.
*/
void getfdbinfo (char *block)
{
timep.modtime = unixtime(block, WdoffFDB_Wrt);
timep.actime = unixtime(block, WdoffFDB_Ref);
timeptr = ctime(& timep.modtime) + 4; /* Skip over day-name field */
timeptr[20] = '\0'; /* Chop off \n at end */
bytesize = getfield(block, WdoffFDB_BSZ, BtoffFDB_BSZ, BtlenFDB_BSZ);
numbytes = getfield(block, WdoffFDB_Size, BtoffFDB_Size,BtlenFDB_Size);
pgcount = getfield(block, WdoffFDB_PGC, BtoffFDB_PGC, BtlenFDB_PGC);
tprot = getfield(block, WdoffFDB_PRT, BtoffFDB_PRT, BtlenFDB_PRT);
archived = getfield(block, WdoffFDB_CTL, BtoffFDB_Arc, BtlenFDB_Arc);
invisible = getfield(block, WdoffFDB_CTL, BtoffFDB_Inv, BtlenFDB_Inv);
offline = getfield(block, WdoffFDB_CTL, BtoffFDB_Off, BtlenFDB_Off);
apgcount = getfield(block, WdoffFDB_PGC_A, BtoffFDB_PGC, BtlenFDB_PGC);
/* The rest is bogus. */
tp1 = getfield(block, WdoffFDB_TP1, 0, 36);
tp2 = getfield(block, WdoffFDB_TP2, 0, 36);
ss1 = getfield(block, WdoffFDB_SS1, BtoffFDB_SS, BtlenFDB_SS);
ss2 = getfield(block, WdoffFDB_SS2, BtoffFDB_SS, BtlenFDB_SS);
tf1 = getfield(block, WdoffFDB_TF1, BtoffFDB_TF, BtlenFDB_TF);
tf2 = getfield(block, WdoffFDB_TF2, BtoffFDB_TF, BtlenFDB_TF);
}
int t2uprot (unsigned int prot)
{
register unsigned tprot, uprot;
register int tshift;
#ifdef notdef
if (f->FB_dir) { /* THIS WON'T WORK! */
punt(0, "Can't handle directory %s", topsname);
prot = gtdirprot(_dirnm(jfn)); /* returns 20 fmt protection */
for (tshift=12, uprot=0; tshift >= 0; tshift -= 6) {
tprot = prot >> tshift; /* pick up next field */
uprot <<= 3;
if (tprot & DP_rd)
uprot |= WREAD|WEXEC; /* world read, world execute */
if (tprot & (DP_cn|DP_cf)) /* allow write for either conn. */
uprot |= WWRITE; /* access or add files access */
}
}
else
#endif
{ /* do it this way so easily modified-- i know it could be faster */
for (tshift=12, uprot=0; tshift >= 0; tshift -= 6) {
tprot = prot >> tshift;
uprot <<= 3;
uprot |= (tprot >> 3) & 07; /* just r,w,x */
}
}
return uprot;
}
void doFileHeader (char *block)
{
char *ts;
static char prt_ar[2] = {'-', 'A'};
static char prt_inv[2] = {'-', 'I'};
static char prt_off[2] = {'-', 'O'};
if (debug > 5)
printf("File Header block:\n");
filenum = getfield(block, WdoffFileNum, BtoffFileNum, BtlenFileNum);
getstring(block, topsname, WdoffFLName, sizeof(topsname));
ts = index(topsname, ';'); /* Chop off ;Pprotection;Aacct */
*ts++ = pendstring(); /* superfluous */
*ts = 0;
folddown(topsname, topsname);
fpFile = NULL;
if ( doallflag ||
(patterns && patternmatch()) ||
(expression && expmatch()) ||
(nselect && fmatch()) ) {
getfdbinfo(block);
pageno = getfield(block, WdoffPageNum, BtoffPageNum, BtlenPageNum);
if (dodir || verbose) {
if (verbose)
printf("%3ld%6ld ", ssno, filenum);
printf("%c%c%c", prt_ar[archived], prt_off[offline],
prt_inv[invisible]);
printf("%5d%9ld %2d %o %s %s", offline ? apgcount : pgcount,
numbytes, bytesize, tprot, timeptr, topsname);
if (archived && verbose >= 2)
printf(" %x%4d%5d %x%4d%5d", tp1, ss1, tf1, tp2, ss2, tf2);
if (pageno != 0)
printf(" Split file, part 2");
}
if (xflg) {
/* Special hack for bad files */
if (textflg && bytesize != 7) {
if (bytesize == 0 || bytesize == 36) {
bytesize = 7;
numbytes *= 5;
}
}
if ((bytesize == 7 || bytesize == 8) && !offline) {
if (pageno != 0) { /* continued file */
int missing = pageno * 512 * (bytesize == 7 ? 5 : 4);
numbytes -= missing;
if (!(dodir || verbose))
printf("%s: Split file, part 2", topsname);
printf(": %d raw bytes missing.", missing);
if (!(dodir || verbose))
putchar('\n');
}
fpFile = fopen(unixname(topsname), "w");
if (fpFile == NULL)
punt(1, "Can't open %s for write", sunixname);
else if (verbose)
printf(" Extracted.");
if (fchmod(fileno(fpFile), t2uprot(tprot) & ~0111) < 0)
punt(1, "fchmod on %s", sunixname);
} else if (verbose)
printf(" Skipping -- %s file.",
offline ? "offline" : "binary");
}
if (dodir || verbose)
putchar('\n');
}
}
/*ARGSUSED*/
void doFileTrailer (char *block)
{
if (debug > 10) printf(" File trailer\n");
if (fpFile != NULL) {
if (pendstring() == '\r')
putc('\r', fpFile);
if (fclose(fpFile) == EOF)
punt(1, "fclose: write error on %s", sunixname);
fpFile = NULL;
utime(sunixname, & timep);
if (numbytes != 0)
printf("%s: Split file, part 1: %ld raw bytes left\n",
topsname, numbytes);
}
}
/*ARGSUSED*/
void doTapeTrailer (char *block)
{
if (debug > 10) printf("Tape Trailer");
}
int compwant(const void *wa1, const void *wa2)
{
const struct want *w1 = wa1;
const struct want *w2 = wa2;
int sdif;
if ((sdif = w1->ssnum - w2->ssnum))
return sdif;
return (w1->fnum - w2->fnum);
}
int main (int argc, char *argv[])
{
char *tape; /* Pathname for tape device/file */
char *tapeblock;
int rc;
int rtype;
if (! (tape = getenv ("TAPE")))
tape = TAPE;
/* Do switch parsing */
while(argc>1 && argv[1][0] == '-'){
switch(argv[1][1]){
case 'f':
if (argc <= 2)
punt(0, "Need filename after -f");
tape = argv[2];
argc--; argv++;
break;
case 'T': /* Force text mode on "binary" files */
textflg = 1;
break;
case 't': /* directory listing */
dodir = 1;
break;
case 'x': /* extract */
xflg = 1;
break;
case 'v': /* verbosity */
verbose++;
break;
case 'g': /* keep gen number */
genflg++;
break;
case 'd':
debug = atoi(&argv[1][2]);
fprintf(stderr, "Debug value set to %d\n", debug);
break;
case 'n': /* numeric output filenames */
if (argc <= 2)
punt(0, "Need number after -n");
number = atoi(argv[2]); /* First file name */
numflg = 1;
argc--; argv++;
break;
case 'c': /* keep CR`s in CR/LF pairs */
keepcr++;
break;
case 'e': /* regular expression */
if (argc <= 2)
punt(0, "Need expression after -e");
if (expression)
punt(0, "Only one regexp allowed");
expression = argv[2];
if ((re_comp_error = re_comp(expression)) != 0)
punt(0, "re_comp: %s", re_comp_error);
argc--; argv++;
break;
case 'S': /* selected save set number */
if (argc <= 2)
punt(0, "Need save set number after -S");
cursswant = atoi(argv[2]);
argc--; argv++;
break;
case 'F': /* selected file numbers */
if (argc <= 2)
punt(0, "Need file number(s) after -F");
for (argc -= 2, argv += 2;
argc && isdigit(**argv);
argc--, argv++, nselect++) {
want[nselect].ssnum = cursswant;
want[nselect].fnum = atoi(*argv);
}
argc += 2; argv -= 2;
break;
default:
punt(0, "unknown flag %s", argv[1]);
}
argc--; argv++;
}
if (!xflg && !dodir)
punt(0, "Need either '-x' or '-t' option.");
if (argc > 1) {
patterns = &argv[1];
numpats = argc - 1;
}
doallflag = !(patterns || expression || nselect);
if (nselect)
qsort((char *)want, nselect, sizeof (struct want), compwant);
tape_handle = opentape (tape, 0, 0);
if (! tape_handle)
punt(1, "Can't open tape '%s'", tape);
rc = 0;
for ( ; ; ) /* Loop till end of tape */
{
/*** Read a block ***/
if (rc == 0) {
rc = getrec (tape_handle, tapeblocka, TAPEBLK);
if (debug > 99)
printf("rc=%d\n", rc);
if ((rc % (518*5)) != 0) {
if (rc != 0)
punt(1, "Oops. Read block len = %d", rc);
}
if (rc == 0) {
if (verbose)
printf("\nEnd of tape.\n");
exit(0); /* Normal exit */
}
tapeblock = tapeblocka;
rc = rc - 518*5;
}
else {
tapeblock = tapeblock + 518*5;
rc = rc - 518*5;
}
/*** Do something with it ***/
switch(rtype = -getfield(tapeblock,
WdoffRectype, BtoffRectype, BtlenRectype))
{
case RectypeData: /* Data block */
doDatablock(tapeblock);
break;
case RectypeTphd: /* Saveset header */
doSaveset(tapeblock, 0);
break;
case RectypeFlhd: /* File header */
doFileHeader(tapeblock);
break;
case RectypeFltr: /* File trailer */
doFileTrailer(tapeblock);
break;
case RectypeTptr: /* Tape trailer */
doTapeTrailer(tapeblock);
break;
case RectypeUsr: /* User directory info ? */
if (verbose >= 3)
fprintf(stderr, "Directory record skipped\n");
break;
case RectypeCtph: /* Continued saveset hdr */
doSaveset(tapeblock, 1);
break;
case RectypeFill: /* Fill record */
if (verbose >= 3)
fprintf(stderr, "Fill record skipped\n");
break;
default:
punt(0, "Unknown record type 0x%x", rtype);
break;
}
}
}
#ifdef notdef
#define HOUR 3600
#define DAY (HOUR*24)
#define DAY0 40587 /* number of days between tops20 0 day and Unix 0 day */
#define makeword(l, r) ( ((l) << 18) | (r) )
#define getright(b) ( (b) & 0777777 )
#define getleft(b) ( (b) >> 18 )
/* Convert Tops-20 to Unix time -- curently incomplete due to 32 < 36 bits */
int
_t2utim(t)
unsigned t;
{
register ticks, rh, secs;
ticks = t - makeword(DAY0, 0);
rh = getright(ticks) * DAY;
secs = rh >> 18;
if (rh % makeword(1,0) > 0400000)
secs++; /* round up */
return (getleft(ticks) * DAY) + secs;
}
#endif

777
t10backup.c Normal file
View File

@ -0,0 +1,777 @@
/*
Dump contents of Tops-10 BACKUP tapes, or tape images.
Original author unknown.
Modified by Eric Smith to use John Wilson's tapeio library.
Copyright 1999 Eric Smith
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as published
by the Free Software Foundation. Note that permission is not granted
to redistribute this program under the terms of any other version of the
General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include "t10backup.h"
#include "tapeio.h"
#define bool long
#define false 0
#define true 1
#define MAX_FILE_SPEC 1024
#define RAWSIZE (5*(32+512))
#define endof(s) (strchr(s, (char) 0))
char *progname;
tape_handle_t tape; /* Source "tape". */
bool eightbit = false; /* Status of -8 (eight-bit) flag. */
bool buildtree = false; /* Status of -d (build trees) flag. */
bool interchange = false; /* Status of -i (interchange) flag. */
long verbose = 0; /* Status of -v (verbose) flag. */
char* argfiles [MAX_FILE_SPEC]; /* File spec's to extract. */
long argcount; /* Number of them. */
unsigned char rawdata[RAWSIZE]; /* Raw data for a tape block. */
long headlh[32], headrh[32]; /* Header block from tape. */
long datalh[512], datarh[512]; /* Data block from tape. */
long prevSEQ; /* SEQ number of previous block. */
long currentfilenumber;
long defercount; /* Count of defered output bytes. */
bool extracting;
FILE* destination;
/* Tape information: */
char systemname[100];
char savesetname[100];
/* File information: */
long a_bsiz; /* For now. */
long a_alls;
long a_mode;
long a_leng;
char filedev[100]; /* Device: */
char filedir[100]; /* [ufd] */
char filename[100]; /* file name. */
char fileext[100]; /* extension. */
char filespec[7][100]; /* [0]: device:ufd. */
/* [1-5]: sfd's, stored directly here. */
/* [6]: file.ext */
char cname[100]; /* Canonical name. */
void print_usage (FILE *f)
{
fprintf (f, "Usage: %s [options] [filespecs...]\n", progname);
fprintf (f, "Options:\n"
" -t list directory\n"
" -x extract files\n"
" -f <file> read input from file\n"
" /dev/* tape drive\n"
" host:file rmt server\n"
" file tape image file\n"
" - tape image from stdin\n"
" -s <range> use save sets in range\n"
" e.g. '-s 2' for only save set 2\n"
" '-s 2,' for save set 2 through EOT\n"
" '-s 3,5' for save sets 3 through 5\n"
" -v verbose\n"
" -vv very verbose\n"
" -8 eight bit mode\n"
" -i interchange mode\n"
" -d build directory tree\n");
}
void fatal (int retval, char *fmt, ...)
{
va_list ap;
if (fmt)
{
fprintf (stderr, "%s: ", progname);
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
}
if (retval == 1)
print_usage (stderr);
exit (retval);
}
void warning (char *fmt, ...)
{
va_list ap;
fprintf (stderr, "%s: ", progname);
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
}
/* unpackheader unpacks the header block from the raw stream. */
void unpackheader (void)
{
unsigned char* rawptr;
long i, left, right;
unsigned char c;
rawptr = & rawdata [0];
for (i = 0; i < 32; i++)
{
left = * (rawptr++) << 10;
left |= * (rawptr++) << 2;
left |= (c = * (rawptr++)) >> 6;
right = (c & 077) << 12;
right |= * (rawptr++) << 4;
right |= * (rawptr++) & 017;
headlh [i] = left;
headrh [i] = right;
if (verbose > 1)
{
printf("\n%li l=%ld, r=%ld", i, left, right);
}
}
}
/* unpackdata unpacks the data block from the raw stream. */
void unpackdata (void)
{
unsigned char* rawptr;
long i, left, right;
unsigned char c;
rawptr = & rawdata [32*5];
for (i = 0; i < 512; i++)
{
left = * (rawptr++) << 10;
left |= * (rawptr++) << 2;
left |= (c = * (rawptr++)) >> 6;
right = (c & 077) << 12;
right |= * (rawptr++) << 4;
right |= * (rawptr++) & 017;
datalh [i] = left;
datarh [i] = right;
}
}
/* pars_5chars reads five ASCII chars from a machine word. */
void pars_5chars (long index, char *store)
{
long l, r;
l = datalh [index];
r = datarh [index];
store [0] = (0177 & (l >> 11));
store [1] = (0177 & (l >> 4));
store [2] = (0177 & ((l << 3) | ((r >> 15) & 017)));
store [3] = (0177 & (r >> 8));
store [4] = (0177 & (r >> 1));
}
/* pars_asciz stores asciz text from data */
void pars_asciz (long index, char *store)
{
long words;
words = datarh [index++];
while ((words--) > 0)
{
pars_5chars (index++, store);
store += 5;
}
* store = (char) 0;
}
/* pars_o_name parses an o$name block from data. */
void pars_o_name (long index)
{
long lastw;
lastw = index + datarh [index];
++index;
while (index < lastw)
{
switch (datalh [index])
{
case 0: index = lastw; break;
case 1: pars_asciz (index, filedev); break;
case 2: pars_asciz (index, filename); break;
case 3: pars_asciz (index, fileext); break;
case 32: pars_asciz (index, filedir); break;
case 33: pars_asciz (index, filespec [1]); break;
case 34: pars_asciz (index, filespec [2]); break;
case 35: pars_asciz (index, filespec [3]); break;
case 36: pars_asciz (index, filespec [4]); break;
case 37: pars_asciz (index, filespec [5]); break;
}
index += datarh [index];
}
}
void pars_o_attr (long index)
{
/* parse off file attribute block */
++index;
a_bsiz = datarh [index + A_BSIZ]; /* for now... */
a_alls = datarh [index + A_ALLS]; /* for now... */
a_mode = datarh [index + A_MODE]; /* for now... */
a_leng = datarh [index + A_LENG]; /* for now... */
}
void pars_o_dirt (long index)
{
/* parse off directory attribute block */
}
void pars_o_sysn (long index)
{
pars_asciz (index, systemname);
}
void pars_o_ssnm (long index)
{
pars_asciz (index, savesetname);
}
void zerotapeinfo (void)
{
systemname [0] = (char) 0;
savesetname [0] = (char) 0;
}
void zerofileinfo (void)
{
filedev [0] = (char) 0;
filedir [0] = (char) 0;
filename [0] = (char) 0;
fileext [0] = (char) 0;
filespec [0][0] = (char) 0;
filespec [1][0] = (char) 0;
filespec [2][0] = (char) 0;
filespec [3][0] = (char) 0;
filespec [4][0] = (char) 0;
filespec [5][0] = (char) 0;
filespec [6][0] = (char) 0;
cname [0] = (char) 0;
}
/* unpackinfo picks non-data information from data block. */
void unpackinfo (void)
{
long index;
unpackdata ();
index = 0;
while (index < headrh [G_LND])
{
switch (datalh [index])
{
case 1: pars_o_name (index); break;
case 2: pars_o_attr (index); break;
case 3: pars_o_dirt (index); break;
case 4: pars_o_sysn (index); break;
case 5: pars_o_ssnm (index); break;
}
index += datarh [index];
}
}
void printtapeinfo (void)
{
if (verbose)
{
if (* savesetname != (char) 0)
printf ("Saveset name: %s\n", savesetname);
if (* systemname != (char) 0)
printf ("Written on: %s\n", systemname);
}
}
void downcase (char *s)
{
while (*s != (char) 0) {
if (isupper(*s)) *s = tolower(*s);
s++;
}
}
void buildfilenames (void)
{
long i;
if (* filedev != (char) 0)
sprintf (filespec [0], "%s:%s", filedev, filedir);
else
sprintf (filespec [0], "%s", filedir);
sprintf (filespec [6], "%s.%s", filename, fileext);
for (i = 0; i < 7; i++)
downcase (filespec [i]);
sprintf (cname, "%s", filespec [0]);
for (i = 1; i < 6; i++)
{
if (* filespec [i] != (char) 0)
sprintf (endof (cname), ".%s", filespec [i]);
}
if (* cname != (char) 0)
sprintf (endof (cname), "..%s", filespec [6]);
else
sprintf (cname, "%s", filespec [6]);
}
void printfileinfo (void)
{
buildfilenames ();
printf ("%3ld %s", currentfilenumber, cname);
if (verbose)
{
printf (" (%ld) alloc:%ld, mode:%lo, len:%ld", a_bsiz, a_alls, a_mode, a_leng);
}
printf ("\n");
}
/* readblock reads one logical block from the input stream. */
/* The header is unpacked into head{l,r}; the data is not. */
int readblock (void)
{
long i;
i = getrec (tape, rawdata, RAWSIZE);
if (i == 0)
return (0);
if (i != RAWSIZE)
{
fprintf (stderr, "record length %ld, expected %d\n", i, RAWSIZE);
while (i++ < RAWSIZE) rawdata [i] = (char) 0;
}
unpackheader ();
return (1);
}
/* Disk file output routines: */
void WriteBlock (void)
{
char buffer [5*512];
long bufpos, index;
unpackdata ();
for (index = headrh [G_LND], bufpos = 0;
index < (headrh [G_LND] + headrh [G_SIZE]); index++)
{
pars_5chars (index, &buffer [bufpos]);
bufpos += 5;
}
if (headlh [G_FLAGS] & GF_EOF)
{
for (index = 1; index < (eightbit ? 4 : 5); index++)
{
if (buffer[bufpos - 1] == (char) 0)
bufpos--;
}
}
(void) fwrite (buffer, sizeof (char), bufpos, destination);
}
/* OpenOutput opens the output file, according to -d and -i flags. */
bool OpenOutput (void)
{
struct stat statbuf;
char oname [100];
long i;
defercount = 0;
if (interchange)
destination = fopen (filespec [6], "w");
else if (! buildtree)
destination = fopen(cname, "w");
else
{
for (i = 0, oname [0] = (char) 0; i < 6; i++)
{
if (* filespec [i] == (char) 0)
break;
sprintf (endof (oname), "%s", filespec [i]);
if (stat (oname, & statbuf) != 0)
{
if (mkdir (oname, 0777) != 0)
{
warning ("cannot create %s/\n", oname);
return (false);
}
}
sprintf (endof (oname), "/");
}
sprintf (endof (oname), "%s", filespec [6]);
destination = fopen (oname, "w");
}
return (destination != NULL);
}
void CloseOutput (void)
{
/* Close output file after us. */
}
/* Argmatch checks if the current file matches the given argument: */
bool argmatch (char *arg)
{
long target;
char* f;
char* p;
char* s;
if (*arg == '#')
{
(void) sscanf (arg, "#%ld", & target);
return (target == currentfilenumber);
}
if (*arg == '*')
return (1);
for (f = cname; *f != (char) 0; f++)
{
for (p = f, s = arg; (*s != (char) 0) && (*p == *s); p++, s++)
;
if (*s == (char) 0)
return (true);
}
return (false);
}
/* doextract performs the job of "backup -x ..." */
int doextract (void)
{
int got_blocks = 0;
long i;
currentfilenumber = 0;
extracting = false;
for (;;)
{
if (! readblock ())
return (got_blocks);
got_blocks = 1;
if (headrh [G_SEQ] == prevSEQ)
continue;
if (headrh [G_TYPE] == T_FILE)
{
if (headlh [G_FLAGS] & GF_SOF)
{
currentfilenumber++;
zerofileinfo ();
unpackinfo ();
buildfilenames ();
for (i = 0; i < argcount; i++)
{
if (argmatch (argfiles [i]))
{
if (*argfiles[i] == '#')
{
/* Maybe do a pure shift here? */
argfiles [i] = argfiles [--argcount];
}
extracting = true;
break;
}
}
if (extracting)
{
if (OpenOutput ())
{
if (verbose)
{
printf ("Extracting %s", cname);
fflush (stdout);
}
}
else
{
warning ("can't open %s for output\n", cname);
extracting = false;
}
}
}
if (extracting)
{
WriteBlock ();
if (headlh [G_FLAGS] & GF_EOF)
{
(void) fclose (destination);
extracting = false;
if (verbose)
printf ("\n");
if (argcount == 0)
{
skipfile (tape, 1);
return (1);
}
}
}
}
prevSEQ = headrh[G_SEQ];
}
}
/* dodirectory performs the job of "backup -t ..." */
int dodirectory (void)
{
int got_blocks = 0;
currentfilenumber = 0;
for (;;)
{
if (! readblock ())
return (got_blocks);
got_blocks = 1;
if (headrh [G_SEQ] == prevSEQ)
continue;
if (headrh [G_TYPE] == T_BEGIN)
{
zerotapeinfo ();
unpackinfo ();
printtapeinfo ();
}
if (headrh [G_TYPE] == T_FILE)
{
if (headlh [G_FLAGS] & GF_SOF)
{
++currentfilenumber;
zerofileinfo();
unpackinfo();
printfileinfo();
}
}
prevSEQ = headrh [G_SEQ];
}
}
/* command decoder and dispatcher */
void checkarg (char *arg)
{
long i;
char c;
if (*arg == '#')
{
if (sscanf (arg, "#%ld%c", & i, & c) != 1)
fatal (1, "bad argument: '%s'\n", arg);
}
}
void parse_range (char *s, int *first, int *last)
{
char *s2 = s;
(* first) = (* last) = strtol (s, & s, 0);
if (! s)
return;
if (*(s++) != ',')
goto badrange;
if (! *s)
{
(* last) = INT_MAX;
return;
}
(* last) = strtol (s, & s, 0);
if (! s)
goto badrange;
if ((* first) > (* last))
goto badrange;
return;
badrange:
fatal (1, "bad range syntax: '%s'\n", s2);
}
int main (int argc, char *argv[])
{
long i;
char action = '\0';
char* inputname = NULL;
char *arg;
int first_save_set = 0;
int last_save_set = 0;
int current_save_set = 0;
progname = argv [0];
argcount = 0;
while (--argc > 0)
{
++argv;
arg = argv [0];
if (arg [0] == '-')
{
while (*++arg)
switch (*arg)
{
case '8':
eightbit = true; break;
case 'd':
buildtree = true; break;
case 'f':
if (--argc < 0)
fatal (1, "input file name missing\n");
inputname = (++argv) [0];
break;
case 'i':
interchange = true; break;
case 's':
if ((--argc < 0) || ((++argv)[0][0] == '-'))
fatal (1, "file skip count missing\n");
parse_range (argv [0], & first_save_set, & last_save_set);
/*
fprintf (stderr, "save set range %d..%d\n",
first_save_set, last_save_set);
*/
break;
case 't':
case 'x':
action = *arg; break;
case 'v':
verbose++; break;
default:
fatal (1, "bad option %c\n", arg [1]);
}
}
else
{
if (argcount >= MAX_FILE_SPEC)
fatal (1, "too many file specs\n");
argfiles [argcount++] = argv [0];
}
}
if (! action)
fatal (1, "either -t or -x must be specified\n");
for (i = 0; i < argcount; i++)
checkarg (argfiles[i]);
if (inputname == NULL)
fatal (1, "no input file given\n");
tape = opentape (inputname, 0, 0);
if (! tape)
fatal (1, "can't open %s for input\n", inputname);
if (first_save_set > 0)
{
skipfile (tape, first_save_set);
current_save_set = first_save_set;
}
while (current_save_set <= last_save_set)
{
int ok = 0;
switch (action)
{
case 't': ok = dodirectory (); break;
case 'x': ok = doextract (); break;
}
if (! ok)
break;
current_save_set++;
}
return (0);
}

80
t10backup.h Normal file
View File

@ -0,0 +1,80 @@
/* Record types: */
#define T_LABEL 1 /* Label. */
#define T_BEGIN 2 /* Start of SaveSet. */
#define T_END 3 /* End of SaveSet. */
#define T_FILE 4 /* File data. */
#define T_UFD 5 /* UFD data. */
#define T_EOV 6 /* End of volume. */
#define T_COMM 7 /* Comment. */
#define T_CONT 8 /* Continuation. */
/* Offsets into header block: */
#define G_TYPE 0 /* Record type. */
#define G_SEQ 1 /* Sequence #. */
#define G_RTNM 2 /* Relative tape #. */
#define G_FLAGS 3 /* Flags: */
#define GF_EOF 0400000 /* End of file. */
#define GF_RPT 0200000 /* Repeat of last record. */
#define GF_NCH 0100000 /* Ignore checksum. */
#define GF_SOF 0040000 /* Start of file. */
#define G_CHECK 4 /* Checksum. */
#define G_SIZE 5 /* Size of data in this block. */
#define G_LND 6 /* Length of non-data block. */
/* Non-data block types: */
#define O_NAME 1 /* File name. */
#define O_ATTR 2 /* File attributes. */
#define O_DIRECT 3 /* Directory attributes. */
#define O_SYSNAME 4 /* System name block. */
#define O_SAVESET 5 /* SaveSet name block. */
/* Offsets in attribute block: */
#define A_FHLN 0 /* header length word */
#define A_FLGS 1 /* flags */
#define A_WRIT 2 /* creation date/time */
#define A_ALLS 3 /* allocated size */
#define A_MODE 4 /* mode */
#define A_LENG 5 /* length */
#define A_BSIZ 6 /* byte size */
#define A_VERS 7 /* version */
#define A_PROT 8 /* protection */
#define A_ACCT 9 /* byte pointer account string */
#define A_NOTE 10 /* byte pointer to anonotation string */
#define A_CRET 11 /* creation date/time of this generation */
#define A_REDT 12 /* last read date/time of this generation */
#define A_MODT 13 /* monitor set last write date/time */
#define A_ESTS 14 /* estimated size in words */
#define A_RADR 15 /* requested disk address */
#define A_FSIZ 16 /* maximum file size in words */
#define A_MUSR 17 /* byte ptr to id of last modifier */
#define A_CUSR 18 /* byte ptr to id of creator */
#define A_BKID 19 /* byte ptr to save set of previous backup */
#define A_BKDT 20 /* date/time of last backup */
#define A_NGRT 21 /* number of generations to retain */
#define A_NRDS 22 /* nbr opens for read this generation */
#define A_NWRT 23 /* nbr opens for write this generation */
#define A_USRW 24 /* user word */
#define A_PCAW 25 /* privileged customer word */
#define A_FTYP 26 /* file type and flags */
#define A_FBSZ 27 /* byte sizes */
#define A_FRSZ 28 /* record and block sizes */
#define A_FFFB 29 /* application/customer word */
/* T_BEGIN, T_END & T_CONT header offsets: */
#define S_DATE 12
#define S_FORMAT 13
#define S_BVER 14
#define S_MONTYP 15
#define S_SVER 16
#define S_APR 17
#define S_DEVICE 18
#define S_MTCHAR 19
#define S_REELID 20
#define S_LTYPE 21

View File

@ -26,10 +26,12 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "tapeio.h"
#include "stdio.h"
#include "stdlib.h"
#include "stdarg.h"
#include "tapeio.h"
#define MAX_REC_LEN 32768
char *progname;
@ -71,8 +73,8 @@ int main (int argc, char *argv[])
int verbose = 0;
char *srcfn = NULL;
char *destfn = NULL;
tape_handle src = NULL;
tape_handle dest = NULL;
tape_handle_t src = NULL;
tape_handle_t dest = NULL;
progname = argv [0];

View File

@ -1,13 +1,6 @@
/*
tapedump
This program uses a modified version of John Wilson's tapeio library to
copy tapes between any of the following:
local tape drives - pathname starting with /dev/
remote tape drives (rmt) - pathname containing a colon
Wilson-format tape image files - any other pathname
Copyright 1999, 2000 Eric Smith
This program is free software; you can redistribute it and/or modify
@ -26,12 +19,19 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "tapeio.h"
#include "stdio.h"
#include "stdarg.h"
#include "stdlib.h"
#include "tapeio.h"
#define MAX_REC_LEN 32768
typedef unsigned int u32; /* non-portable!!! */
typedef unsigned char uchar;
char *progname;
void print_usage (FILE *f)
@ -60,9 +60,9 @@ void fatal (int retval, char *fmt, ...)
#define BYTES_PER_LINE 16
void dump (FILE *f, char *buf, int len)
void dump (FILE *f, uchar *buf, u32 len)
{
int i, j;
u32 i, j;
for (i = 0; i < len; i += BYTES_PER_LINE)
{
@ -75,9 +75,9 @@ void dump (FILE *f, char *buf, int len)
for (j = 0; j < BYTES_PER_LINE; j++)
if ((i + j) < len)
{
char c = buf [i+j] & 0x7f;
if ((buf [i+j] >= ' ') && (buf [i+j] <= '~'))
fprintf (f, "%c", buf [i+j]);
uchar c = buf [i+j] & 0x7f;
if ((c >= ' ') && (c <= '~'))
fprintf (f, "%c", c);
else
fprintf (f, ".");
}
@ -92,36 +92,202 @@ void dump (FILE *f, char *buf, int len)
#define MAX_FN_LEN 6
void dump_hp_2000_file_header (FILE *f, char *buf)
u32 get16 (uchar *buf, u32 offset)
{
unsigned long uid;
return ((buf [offset] & 0xff) << 8) | (buf [offset+1] & 0xff);
}
char * file_flag_names [16] =
{
"unrestricted",
"protected",
"locked",
"private",
"unused",
"unused",
"unused",
"unused",
"unused",
"unused",
"unused",
"fcp",
"mwa",
"pfa",
"output",
"input"
};
void dump_hp_2000_directory_entry (FILE *f, uchar *buf)
{
u32 uid;
u32 flags;
char uids [6];
char filename [MAX_FN_LEN + 1];
int i;
uid = ((buf [0] & 0xff) << 8) | (buf [1] & 0xff);
uid = get16 (buf, 0);
if (uid == 0xffff)
{
fprintf (f, "End of directory\n");
return;
}
uids [0] = (uid / 1024) + '@';
sprintf (& uids [1], "%03d", uid & 0x3ff);
for (i = 0; i < MAX_FN_LEN; i++)
filename [i] = buf [i+2] & 0x7f;
filename [sizeof (filename) - 1] = '\0';
fprintf (f, " id %s filename '%s'\n", uids, filename);
fprintf (f, "ID %s filename '%s'", uids, filename);
if (buf [2] & 0x80)
fprintf (f, " ASCII");
if (buf [4] & 0x80)
fprintf (f, " file");
if (buf [2] & 0x80)
fprintf (f, " semi-compiled");
flags = get16 (buf, 14);
for (i = 15; i >= 0; i--)
{
if (flags & (1 << i))
fprintf (f, " %s", file_flag_names [i]);
}
fprintf (f, "\n");
}
void dump_hp_2000_hibernate_file_header (FILE *f, uchar *buf, u32 len)
{
if (len < 24)
{
fprintf (f, "file header too short for directory entry\n");
return;
}
dump_hp_2000_directory_entry (f, buf);
}
void dump_hp_2000_hibernate (FILE *f, u32 file, u32 record, uchar *buf, u32 len)
{
if ((file > 0) && (record == 0))
dump_hp_2000_hibernate_file_header (f, buf, len);
}
static int mcp_file_type = 0;
void dump_hp_2000_mcp_file_header (FILE *f, uchar *buf, u32 len)
{
u32 checksum = 0;
u32 info;
int i;
if (len != 10)
{
fprintf (f, "wrong MCP file header length\n");
return;
}
if ((get16 (buf, 0) != 01000) || (get16 (buf, 2) != 02001))
{
fprintf (f, "wrong MCP file header magic numbers\n");
return;
}
fprintf (f, "MCP file ID word %d ", get16 (buf, 4));
info = get16 (buf, 6);
switch (info)
{
case 0x0000:
fprintf (f, "abs");
break;
case 0x8000:
fprintf (f, "rel");
break;
default:
fprintf (f, "info word %d", info);
break;
}
mcp_file_type = info;
for (i = 2; i < 8; i += 2)
checksum += get16 (buf, i);
checksum &= 0xffff;
if (checksum != get16 (buf, 8))
fprintf (f, ", bad MCP file header checksum");
fprintf (f, "\n");
}
void dump_hp_2000_abs_record (FILE *f, uchar *buf, u32 len)
{
u32 l;
u32 checksum = 0;
u32 tape_checksum;
u32 i;
l= buf [0] * 2 + 6;
if ((l != len) || (buf [1] != 0))
{
fprintf (f, "first word of record is not length (expected %d)\n", l);
return;
}
for (i = 2; i < len - 2; i += 2)
checksum += get16 (buf, i);
checksum &= 0xffff;
tape_checksum = get16 (buf, len - 2);
if (checksum != tape_checksum)
fprintf (f, "computed checksum %04x, tape checksum %04x\n", checksum,
tape_checksum);
}
void dump_hp_2000_rel_record (FILE *f, uchar *buf, u32 len)
{
u32 l;
l= buf [0] * 2;
if ((l != len) || (buf [1] != 0))
{
fprintf (f, "first word of record is not length (expected %d)\n", l);
return;
}
}
void dump_hp_2000_mcp (FILE *f, u32 file, u32 record, uchar *buf, u32 len)
{
if ((len == 10) && (buf [0] == 0x02) && (buf [1] == 0x00) &&
(buf [2] == 0x04) && (buf [3] == 0x01))
{
dump_hp_2000_mcp_file_header (f, buf, len);
}
else switch (mcp_file_type)
{
case 0x0000:
dump_hp_2000_abs_record (f, buf, len);
break;
case 0x8000:
dump_hp_2000_rel_record (f, buf, len);
break;
default:
fprintf (f, "unknown file type\n");
}
}
#endif /* HP_2000_SUPPORT */
typedef enum {
generic,
#ifdef HP_2000_SUPPORT
hp_2000_hibernate,
hp_2000_mcp,
#endif /* HP_2000_SUPPORT */
} t_tape_type;
int main (int argc, char *argv[])
{
int file = 0;
int record = 0;
unsigned long filebytes = 0;
unsigned long tapebytes = 0;
int len;
u32 file = 0;
u32 record = 0;
u32 filebytes = 0;
u32 tapebytes = 0;
u32 len;
char *srcfn = NULL;
tape_handle src = NULL;
char *buf;
#ifdef HP_2000_SUPPORT
int hp_2000_hibernate = 0;
#endif /* HP_2000_SUPPORT */
tape_handle_t src = NULL;
uchar *buf;
t_tape_type tape_type = generic;
progname = argv [0];
@ -131,7 +297,9 @@ int main (int argc, char *argv[])
{
#ifdef HP_2000_SUPPORT
if (argv [0][1] == 'h')
hp_2000_hibernate++;
tape_type = hp_2000_hibernate;
else if (argv [0][1] == 'm')
tape_type = hp_2000_mcp;
else
#endif /* HP_2000_SUPPORT */
fatal (1, "unrecognized option '%s'\n", argv [0]);
@ -145,7 +313,7 @@ int main (int argc, char *argv[])
if (! srcfn)
fatal (1, NULL);
buf = (char *) malloc (MAX_REC_LEN);
buf = (uchar *) malloc (MAX_REC_LEN);
if (! buf)
fatal (2, "can't allocate buffer\n");
@ -170,10 +338,19 @@ int main (int argc, char *argv[])
else
{
printf ("file %d record %d: length %d\n", file, record, len);
switch (tape_type)
{
#ifdef HP_2000_SUPPORT
if (hp_2000_hibernate && (file > 0) && (record == 0) && (len >= 24))
dump_hp_2000_file_header (stdout, buf);
case hp_2000_hibernate:
dump_hp_2000_hibernate (stdout, file, record, buf, len);
break;
case hp_2000_mcp:
dump_hp_2000_mcp (stdout, file, record, buf, len);
break;
#endif /* HP_2000_SUPPORT */
default:
break;
}
dump (stdout, buf, len);
fflush (stdout);
filebytes += len;

View File

@ -138,7 +138,7 @@ static void doread (int handle, char *buf, int len)
/* get response from "rmt" server */
static int response (tape_handle mtape)
static int response (tape_handle_t mtape)
{
char c, rc;
int n;
@ -177,7 +177,7 @@ static int response (tape_handle mtape)
/* send ioctl() command to local or remote tape drive */
static int doioctl (tape_handle mtape, struct mtop *op)
static int doioctl (tape_handle_t mtape, struct mtop *op)
{
int len;
@ -195,15 +195,15 @@ static int doioctl (tape_handle mtape, struct mtop *op)
/* open the tape drive (or whatever) */
/* "create" =1 to create if file, "writable" =1 to open with write access */
tape_handle opentape (char *name, int create, int writable)
tape_handle_t opentape (char *name, int create, int writable)
{
char *p, *user, *port;
int len;
char *host = NULL;
tape_handle mtape = NULL;
tape_handle_t mtape = NULL;
mtape = (tape_handle) calloc (1, sizeof (struct mtape_t));
mtape = (tape_handle_t) calloc (1, sizeof (struct mtape_t));
if (! mtape)
FAIL ("?can't allocate mtape struct\n");
@ -320,7 +320,7 @@ tape_handle opentape (char *name, int create, int writable)
/* close the tape drive */
void closetape (tape_handle mtape)
void closetape (tape_handle_t mtape)
{
if (mtape->waccess)
{ /* opened for create/append */
@ -346,7 +346,7 @@ void closetape (tape_handle mtape)
/* rewind tape */
void posnbot (tape_handle mtape)
void posnbot (tape_handle_t mtape)
{
if (mtape->tape_type == TT_IMAGE)
{ /* image file */
@ -368,7 +368,7 @@ void posnbot (tape_handle mtape)
/* position tape at EOT (between the two tape marks) */
void posneot (tape_handle mtape)
void posneot (tape_handle_t mtape)
{
if (mtape->tape_type == TT_IMAGE)
{ /* image file */
@ -408,7 +408,7 @@ void posneot (tape_handle mtape)
/* read a tape record, return actual length (0=tape mark) */
int getrec (tape_handle mtape, char *buf, int len)
int getrec (tape_handle_t mtape, char *buf, int len)
{
unsigned char byte [4]; /* 32 bits for length field(s) */
unsigned long l; /* at least 32 bits */
@ -465,7 +465,7 @@ int getrec (tape_handle mtape, char *buf, int len)
/* write a tape record */
void putrec (tape_handle mtape, char *buf, int len)
void putrec (tape_handle_t mtape, char *buf, int len)
{
unsigned char l [4];
@ -495,7 +495,7 @@ void putrec (tape_handle mtape, char *buf, int len)
/* write a tape mark */
void tapemark (tape_handle mtape)
void tapemark (tape_handle_t mtape)
{
static char zero [4] = { 0, 0, 0, 0 };
@ -516,7 +516,7 @@ void tapemark (tape_handle mtape)
/* skip records (negative for reverse) */
void skiprec (tape_handle mtape, int count)
void skiprec (tape_handle_t mtape, int count)
{
unsigned char byte [4]; /* 32 bits for length field(s) */
unsigned long l; /* at least 32 bits */
@ -566,7 +566,7 @@ void skiprec (tape_handle mtape, int count)
/* skip forward to the next file mark, and leave the tape positioned
after the mark */
static void skip_to_mark (tape_handle mtape)
static void skip_to_mark (tape_handle_t mtape)
{
unsigned char byte [4]; /* 32 bits for length field(s) */
unsigned long l; /* at least 32 bits */
@ -627,7 +627,7 @@ static void skip_to_mark (tape_handle mtape)
/* skip files (negative for reverse) */
void skipfile (tape_handle mtape, int count)
void skipfile (tape_handle_t mtape, int count)
{
if (mtape->tape_type != TT_IMAGE)
{

View File

@ -21,33 +21,33 @@
*/
typedef struct mtape_t *tape_handle; /* opaque type */
typedef struct mtape_t *tape_handle_t; /* opaque type */
/* open a tape drive */
tape_handle opentape (char *name, int create, int writable);
tape_handle_t opentape (char *name, int create, int writable);
/* close a tape drive */
void closetape (tape_handle h);
void closetape (tape_handle_t h);
/* rewind tape */
void posnbot (tape_handle h);
void posnbot (tape_handle_t h);
/* position tape at EOT (between the two tape marks) */
void posneot (tape_handle h);
void posneot (tape_handle_t h);
/* read a tape record, return actual length (0=tape mark) */
int getrec (tape_handle h, char *buf, int len);
int getrec (tape_handle_t h, char *buf, int len);
/* write a tape record */
void putrec (tape_handle h, char *buf, int len);
void putrec (tape_handle_t h, char *buf, int len);
/* write a tape mark */
void tapemark (tape_handle h);
void tapemark (tape_handle_t h);
/* skip records (negative for reverse) */
void skiprec (tape_handle h, int count);
void skiprec (tape_handle_t h, int count);
/* skip files (negative for reverse) */
void skipfile (tape_handle h, int count);
void skipfile (tape_handle_t h, int count);