mirror of
https://github.com/PDP-10/klh10.git
synced 2026-02-06 00:15:26 +00:00
1579 lines
43 KiB
C
1579 lines
43 KiB
C
/* TAPEDD.C - Utility to copy tapes
|
||
*/
|
||
/* $Id: tapedd.c,v 2.5 2002/03/28 16:52:31 klh Exp $
|
||
*/
|
||
/* Copyright © 1992, 1993, 2001 Kenneth L. Harrenstien
|
||
** All Rights Reserved
|
||
**
|
||
** This file is part of the KLH10 Distribution. Use, modification, and
|
||
** re-distribution is permitted subject to the terms in the file
|
||
** named "LICENSE", which contains the full text of the legal notices
|
||
** and should always accompany this Distribution.
|
||
**
|
||
** This software is provided "AS IS" with NO WARRANTY OF ANY KIND.
|
||
**
|
||
** This notice (including the copyright and warranty disclaimer)
|
||
** must be included in all copies or derivations of this software.
|
||
*/
|
||
/*
|
||
* $Log: tapedd.c,v $
|
||
* Revision 2.5 2002/03/28 16:52:31 klh
|
||
* First pass at using LFS (Large File Support)
|
||
*
|
||
* Revision 2.4 2002/03/10 22:17:02 klh
|
||
* Fix LOGTAPE macro for virtual format case
|
||
*
|
||
* Revision 2.3 2001/11/10 21:28:59 klh
|
||
* Final 2.0 distribution checkin
|
||
*
|
||
*/
|
||
|
||
/* TAPEDD is mainly used to copy tapes onto disk and make
|
||
** further tape copies, either directly from the original tapes
|
||
** or from the disk version. It was previously called TDCOPY;
|
||
** TAPEDD is its new, portable incarnation AND HAS NOT BEEN
|
||
** TESTED very much yet. Be careful!
|
||
**
|
||
** In order to represent the disk version accurately, a "tape directory"
|
||
** is used to describe the format of the raw data, including record
|
||
** lengths and tapemarks. This permits anything that understands the
|
||
** tape directory to read the raw-data file as if it were an actual tape.
|
||
**
|
||
** In the absence of properly implemented pseudo-devices on Unix,
|
||
** the next step would be to provide a library-type interface such that
|
||
** the routines can be applied either to on-disk files or actual tapes,
|
||
** without needing to know which is which. VMTAPE.C is a step towards
|
||
** this; OSDTAP.C could complete it.
|
||
*/
|
||
|
||
#include <string.h>
|
||
#include <fcntl.h>
|
||
#include <signal.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h> /* exit() */
|
||
#include <errno.h>
|
||
#include <stdarg.h> /* For error-reporting functions */
|
||
#include <sys/file.h> /* For open() flags */
|
||
|
||
#include "rcsid.h"
|
||
#include "cenv.h"
|
||
#include "vmtape.h" /* Include virtual magtape stuff */
|
||
|
||
#if CENV_SYS_T20
|
||
# include <jsys.h>
|
||
# include <macsym.h> /* FLD macros */
|
||
# define char8 _KCCtype_char8
|
||
# define HAVE_STRERROR 1
|
||
# define NULLDEV "NUL:"
|
||
# define FD_STDIN 0
|
||
# define FD_STDOUT 1
|
||
|
||
#elif CENV_SYS_UNIX
|
||
# include <unistd.h> /* Basic Unix syscalls */
|
||
# include <sys/types.h>
|
||
# include <sys/ioctl.h>
|
||
# if HAVE_SYS_MTIO_H
|
||
# include <sys/mtio.h>
|
||
# endif /* HAVE_SYS_MTIO_H */
|
||
# define char8 unsigned char
|
||
# define O_BSIZE_8 0
|
||
# define NULLDEV "/dev/null"
|
||
# define FD_STDIN 0
|
||
# define FD_STDOUT 1
|
||
# define strCMP strcmp /* Temporary compat hack */
|
||
#endif
|
||
|
||
#if HAVE_ERRNO_H
|
||
# include <errno.h>
|
||
#endif
|
||
|
||
|
||
#define MAXRECSIZE (1L<<16) /* was ((15*518*5)+512) */
|
||
#define FNAMSIZ 200
|
||
|
||
#define TRUE 1
|
||
#define FALSE 0
|
||
|
||
#ifdef RCSID
|
||
RCSID(tapedd_c,"$Id: tapedd.c,v 2.5 2002/03/28 16:52:31 klh Exp $")
|
||
#endif
|
||
|
||
/* For now, must include VMT source directly, so as to avoid compile-time
|
||
** switch conflicts (eg with KLH10).
|
||
*/
|
||
#include "vmtape.c"
|
||
|
||
/* New TAPEDD parameters */
|
||
|
||
char nusage[] = "\
|
||
Usage: tapedd <params>\n\
|
||
itX=<path> (Required) Input Tape device, where 'X' is optional\n\
|
||
drive spec: (defaults to 'h')\n\
|
||
h - Half-inch magtape drive (default)\n\
|
||
q - QIC (quarter-inch cartridge) drive\n\
|
||
8 - 8mm drive\n\
|
||
4 - 4mm DAT drive\n\
|
||
vF - Virtual tape & drive, where 'F' is optional\n\
|
||
format spec: (defaults based on file extension)\n\
|
||
r - Raw format, paired with control file\n\
|
||
s - TPS format\n\
|
||
e - TPE format\n\
|
||
c - TPC format\n"
|
||
#if VMTAPE_ITSDUMP
|
||
" i - (read-only) ITS DUMP tapedir\n"
|
||
#endif
|
||
#if VMTAPE_T20DUMP
|
||
" 6 - (read-only) TOPS-10/20 DUMPER V6 tapedir\n"
|
||
#endif
|
||
"\
|
||
otX=<path> (Required) Output Tape device, X as above\n\
|
||
{i,o}c=<path> alternate tape Control file (old id=,od=)\n\
|
||
{i,o}f=<path> alternate raw data file\n\
|
||
{i,o,}bs=<n> Block size (record length)\n\
|
||
log=<path> Log filespec (defaults to stderr)\n\
|
||
rskip=<#> Skip # input records\n\
|
||
fskip=<#> Skip # input files/tapemarks\n\
|
||
rcnt=<#> Max # records to write\n\
|
||
fcnt=<#> Max # files/tapemarks to write\n\
|
||
peot Use physical EOT, ignore logical EOT (double tapemark)\n\
|
||
test Parse control file, output result to stdout\n\
|
||
verbose Verbose\n\
|
||
";
|
||
|
||
|
||
/* Elaboration on new TAPEDD parameter switches:
|
||
|
||
--REQUIRED--
|
||
{i,o}t*=<path> Tape path spec (implies both control & data)
|
||
--OPTIONAL--
|
||
{i,o}f=<path> Tape data spec
|
||
{i,o}c=<path> Tape control (description) spec
|
||
|
||
Defaults:
|
||
If a device is specified (one of 4/8/q/h), no control file is used
|
||
unless one is specified with {i/o}d.
|
||
An {i/o}f spec is redundant but will change the path without
|
||
changing the device type.
|
||
If a virtual tape is specified (v), both the control file and
|
||
the data file pathnames are derived from that spec.
|
||
Either derivation may be overriden with the appropriate
|
||
additional spec ({i/o}f or {i/o}d).
|
||
Possible special cases:
|
||
If no ot*= spec is given, data output is assumed to be the null device.
|
||
This is mainly useful when generating a tape-desc file from
|
||
the input; od= can specify where it goes.
|
||
of= is ignored.
|
||
If no it*= spec is given, no data is input, although a tape-desc
|
||
file can be given with id=.
|
||
if= is ignored.
|
||
|
||
Thus, the equivalent of "test" is:
|
||
tapedd id=foo od=
|
||
|
||
Whenever a null filename is specified (eg with "of="), it is understood
|
||
to mean standard input or output (depending on whether the spec
|
||
was for input or output).
|
||
|
||
More special cases (the user is not expected to know this, but in case
|
||
any of them are tried, this table is an attempt to figure out what
|
||
would be logical behavior). Note "itD=" means "it{h,q,8,4}" as opposed
|
||
to "itv=".
|
||
|
||
COMMAND-LINE DEV
|
||
SPECS STRUCT INTERPRETATION
|
||
it*= id= if= p d r
|
||
- - - - - - Error: No input of any kind (null tape)
|
||
- - x - - - Error, ambiguous (later can test or make assumptions)
|
||
- tdr - - d - Error unless also have od= (testing tapedesc parse)
|
||
- tdr x - - - Error, ambiguous (later can test or make assumptions)
|
||
itD=T - - T - T OK: Reads device: type D, dev T.
|
||
itD=T - xdv T - x OK: Reads device: type D, dev x. (T ignored)
|
||
itD=T tdr - T d T OK: Reads device: type D, dev T, checks vs dir d [*]
|
||
itD=T tdr xdv T d x OK: Reads device: type D, dev x, checks vs dir d [*]
|
||
itv=V - - V Vd Vr OK: Reads virtual: dir Vd, raw Vr.
|
||
itv=V - raw V Vd r OK: Reads virtual: dir Vd, raw r.
|
||
itv=V tdr - V d Vr OK: Reads virtual: dir d, raw Vr.
|
||
itv=V tdr raw V d r OK: Reads virtual: dir d, raw r.
|
||
|
||
[*] = Reads records of sizes and numbers
|
||
given by tapedir; warns if any clashes?
|
||
Or scan normally, cross-check vs tapedir.
|
||
|
||
ot*= od= of= p d r
|
||
- - - - - - Error: No output of any kind (useless)
|
||
- - x - - - Error, ambiguous? (or test or assume 1/2"?)
|
||
- tdr - - d - OK: just outputs tapedir.
|
||
- tdr x - - - Error, ambiguous? (or test or assume 1/2"?)
|
||
otD=T - - T - T OK: Writes device: type D, dev T.
|
||
otD=T - xdv T - x OK: Writes device: type D, dev x. (T ignored)
|
||
otD=T tdr - T d T OK: Writes device: type D, dev T, plus tapedir d
|
||
otD=T tdr xdv T d x OK: Writes device: type D, dev x, plus tapedir d
|
||
otv=V - - V Vd Vr OK: Writes virtual: dir Vd, raw Vr.
|
||
otv=V - raw V Vd r OK: Writes virtual: dir Vd, raw r.
|
||
otv=V tdr - V d Vr OK: Writes virtual: dir d, raw Vr.
|
||
otv=V tdr raw V d r OK: Writes virtual: dir d, raw r.
|
||
|
||
|
||
*/
|
||
#if 0 /* Old TDCOPY switches, for posterity */
|
||
char usage[] = "\
|
||
Usage: tdcopy <sws> <tapedir filespec>\n\
|
||
-r Read tape only (make tapedir, no copy)\n\
|
||
-w Write tape (from tapedir)\n\
|
||
-c Copy tapes directly\n\
|
||
-t Test tapedir (parse tapedir, rewrite to stdout)\n\
|
||
-i <path> Input filespec (- is stdin)\n\
|
||
-o <path> Output filespec (- is stdout)\n\
|
||
-l <path> Log filespec (required if -i or -o use -)\n\
|
||
-q Previous -i or -o filespec is QIC drive\n\
|
||
-h Previous -i or -o filespec is 1/2\" drive\n\
|
||
-8 Previous -i or -o filespec is 8mm drive\n\
|
||
-b <#> Block (record) size to use (esp for QIC)\n\
|
||
-v Verbose\n\
|
||
-n <#> # of records to do\n\
|
||
-m <#> # of files (tapemarks) to do\n\
|
||
-p Use physical EOT (ignore logical)\n\
|
||
";
|
||
#endif /* 0 */
|
||
|
||
|
||
typedef unsigned long rsiz_t; /* Type to hold size of record */
|
||
|
||
/* Switch parameters */
|
||
int sw_tdtest = FALSE;
|
||
int sw_peot = FALSE;
|
||
int sw_verbose = FALSE;
|
||
long sw_maxrec = 0;
|
||
long sw_maxfile = 0;
|
||
long sw_recskip = 0;
|
||
long sw_fileskip = 0;
|
||
rsiz_t sw_bothsiz = 0;
|
||
char *sw_logpath = NULL;
|
||
FILE *logfile = NULL;
|
||
|
||
struct dev {
|
||
char *d_pname; /* Print name, "In" or "Out" */
|
||
char *d_path; /* Tape drive path spec */
|
||
char *d_cpath; /* Control (descriptor) path spec */
|
||
char *d_rpath; /* Raw data path spec */
|
||
int d_istape; /* Tape device (hardware or virtual) */
|
||
int d_vfmt; /* If virtual, tape format to use */
|
||
rsiz_t d_recsiz; /* Block (record) size to use */
|
||
char8 *d_buff, *d_iop; /* Ptr to buffer loc, ptr into buffer */
|
||
rsiz_t d_blen, d_buse; /* Actual buffer length, # chars used */
|
||
int d_bisdyn; /* True if buff dynamically allocated */
|
||
struct vmtape d_vmt; /* Virtual magtape info */
|
||
|
||
/* Hardware tape info */
|
||
int d_fd;
|
||
long mta_herr, /* Hard errors (unrecoverable) */
|
||
mta_serr, /* Soft errors (includes retries) */
|
||
mta_frms; /* # frames (bytes) read in record */
|
||
int mta_bot, /* TRUE if BOT seen */
|
||
mta_eot, /* TRUE if EOT seen */
|
||
mta_eof, /* TRUE if EOF (tapemark) seen */
|
||
mta_retry; /* # times to retry a failing op */
|
||
|
||
vmtpos_t d_tloc; /* Location in bytes read/written from BOT */
|
||
long d_recs, /* # recs in tape so far */
|
||
d_frecs, /* # recs in current file so far */
|
||
d_files, /* # files (tapemarks) seen so far */
|
||
d_herrs, /* Hard errors (unrecoverable) */
|
||
d_serrs; /* Soft errors (includes retries) */
|
||
};
|
||
struct dev dvi = { "In" };
|
||
struct dev dvo = { "Out" };
|
||
|
||
/* Values for d_istape. Those for d_vfmt come from vmtape.h */
|
||
#define MTYP_NULL 0 /* Null device */
|
||
#define MTYP_VIRT 1 /* Virtual magtape, d_vfmt has format */
|
||
#define MTYP_HALF 2 /* Half-inch reel magtape */
|
||
#define MTYP_QIC 3 /* Quarter-inch cartridge magtape */
|
||
#define MTYP_8MM 4 /* 8mm cartridge magtape */
|
||
#define MTYP_4MM 5 /* 4mm DDS/DAT cartridge magtape */
|
||
char *mtypstr[] = {
|
||
"nulldev",
|
||
"virtual",
|
||
"1/2\"",
|
||
"QIC",
|
||
"8mm",
|
||
"4mm"
|
||
};
|
||
|
||
|
||
int cmdsget(int ac, char **av);
|
||
int docopy(void);
|
||
|
||
int devbuffer(struct dev *d, char *buffp, rsiz_t blen);
|
||
int devopen(struct dev *d, int wrtf);
|
||
int devclose(struct dev *d);
|
||
int devread(struct dev *d);
|
||
int devwrite(struct dev *d, unsigned char *buff, rsiz_t len);
|
||
|
||
int devwerr(struct dev *d, int err);
|
||
int devweof(struct dev *d);
|
||
int devweot(struct dev *d);
|
||
|
||
int os_mtopen(struct dev *dp, int wrtf);
|
||
int os_mtread(struct dev *dp);
|
||
int os_mtfsr(struct dev *dp);
|
||
void os_mtstatus(struct dev *dp, FILE *f);
|
||
int os_mtclose(struct dev *dp);
|
||
int os_mtwrite(struct dev *dp);
|
||
int os_mtweof(struct dev *dp);
|
||
void os_mtclrerr(int fd);
|
||
|
||
void errhan(void *arg, struct vmtape *t, char *s);
|
||
void efatal(char *errmsg);
|
||
void swerror(char *fmt, ...);
|
||
|
||
#if CENV_SYS_T20
|
||
void t20status(struct dev *d, FILE *f, int swd, int cnt);
|
||
#endif
|
||
|
||
#if 0
|
||
static char *dupcstr(); /* Use the one from vmtape.c for now */
|
||
#endif
|
||
|
||
/* Error handling */
|
||
|
||
/* Copied from OSDSUP.C */
|
||
|
||
char *
|
||
os_strerror(int err)
|
||
{
|
||
if (err == -1 && errno != err)
|
||
return os_strerror(errno);
|
||
#if HAVE_STRERROR
|
||
return strerror(err);
|
||
#else
|
||
# if HAVE_SYS_ERRLIST
|
||
{
|
||
# if DECL_SYS_ERRLIST
|
||
extern int sys_nerr;
|
||
extern char *sys_errlist[];
|
||
# endif
|
||
if (0 < err && err <= sys_nerr)
|
||
return sys_errlist[err];
|
||
}
|
||
# endif /* HAVE_SYS_ERRLIST */
|
||
if (err == 0)
|
||
return "No error";
|
||
else {
|
||
static char ebuf[30];
|
||
sprintf(ebuf, "Unknown-error-%d", err);
|
||
return ebuf;
|
||
}
|
||
#endif /* !HAVE_STRERROR */
|
||
}
|
||
|
||
void errhan(void *arg, struct vmtape *t, char *s)
|
||
{
|
||
fprintf(logfile, "; %s: %s\n", t->mt_devname, s);
|
||
}
|
||
|
||
|
||
void efatal(char *errmsg) /* print error message and exit */
|
||
/* error message string */
|
||
{
|
||
fflush(stdout);
|
||
fprintf(logfile, "\n?%s\n",errmsg);
|
||
exit(1);
|
||
}
|
||
|
||
|
||
static int do_tdtest(void);
|
||
|
||
int
|
||
main(int argc, char **argv)
|
||
{
|
||
register struct dev *d;
|
||
int ret;
|
||
|
||
logfile = stderr;
|
||
signal(SIGINT, exit); /* Allow int to terminate log files etc */
|
||
|
||
vmt_init(&dvi.d_vmt, "TapeIn");
|
||
dvi.d_vmt.mt_errhan = errhan;
|
||
dvi.d_vmt.mt_errarg = &dvi.d_vmt;
|
||
vmt_init(&dvo.d_vmt, "TapeOut");
|
||
dvo.d_vmt.mt_errhan = errhan;
|
||
dvo.d_vmt.mt_errarg = &dvo.d_vmt;
|
||
|
||
if ((ret = cmdsget(argc, argv))) /* Parse and handle command line */
|
||
exit(ret);
|
||
|
||
|
||
if (!sw_logpath)
|
||
logfile = stderr;
|
||
else {
|
||
if ((logfile = fopen(sw_logpath, "w")) == NULL) {
|
||
logfile = stderr;
|
||
fprintf(logfile, "; Cannot open log file \"%s\", using stderr.\n",
|
||
sw_logpath);
|
||
}
|
||
}
|
||
|
||
/* Special test? */
|
||
if (sw_tdtest) {
|
||
exit(do_tdtest());
|
||
}
|
||
|
||
/* Set up defaults for devices, and log all params if requested */
|
||
|
||
/* bs=, if specified, becomes default for both in and out */
|
||
if (!dvi.d_recsiz) dvi.d_recsiz = sw_bothsiz;
|
||
if (!dvo.d_recsiz) dvo.d_recsiz = sw_bothsiz;
|
||
if (sw_verbose) {
|
||
#define LOGTAPE(d,name) \
|
||
fprintf(logfile, "; %s tape spec \"%s\" (Type: %s", \
|
||
name, (d).d_path, mtypstr[(d).d_istape]); \
|
||
if ((d).d_istape == MTYP_VIRT) \
|
||
fprintf(logfile, " format: %s", vmt_fmtname((d).d_vfmt)); \
|
||
fprintf(logfile, ")\n")
|
||
|
||
LOGTAPE(dvi, " Input");
|
||
LOGTAPE(dvo, "Output");
|
||
#undef LOGTAPE
|
||
|
||
if (dvi.d_recsiz)
|
||
fprintf(logfile, "; Input record size %ld\n", (long)dvi.d_recsiz);
|
||
if (dvo.d_recsiz)
|
||
fprintf(logfile, "; Output record size %ld\n", (long)dvo.d_recsiz);
|
||
if (sw_maxfile)
|
||
fprintf(logfile, "; Max tapemarks (files) to process: %ld\n", sw_maxfile);
|
||
if (sw_maxrec)
|
||
fprintf(logfile, "; Max records to process: %ld\n", sw_maxrec);
|
||
if (sw_logpath)
|
||
fprintf(logfile, "; Using logging path %s\n", sw_logpath);
|
||
}
|
||
|
||
/* Open I/O files as appropriate */
|
||
if (!devopen(&dvi, FALSE)) /* Open for reading */
|
||
exit(1);
|
||
if (!devopen(&dvo, TRUE)) /* Open for writing */
|
||
exit(1);
|
||
|
||
/* Set up buffering. This is somewhat tricky due to all the
|
||
** possible situations; future parameters may complicate it by specifying
|
||
** record truncation, padding, or conversion of some kind.
|
||
** For now, the primary intent is to ensure we have a buffer large
|
||
** enough to handle the maximum expected record size, while still
|
||
** retaining the capability of asking for less than the maximum
|
||
** possible (64K) in case memory usage is a concern (less and less likely
|
||
** these days).
|
||
**
|
||
** Since there are no funny parameters, currently just one buffer
|
||
** is used, whichever of input or output is larger.
|
||
*/
|
||
if (dvi.d_blen < dvo.d_blen)
|
||
dvi.d_blen = dvo.d_blen;
|
||
else if (dvi.d_blen > dvo.d_blen)
|
||
dvo.d_blen = dvi.d_blen;
|
||
|
||
if (!devbuffer(&dvi, (char *)NULL, dvi.d_blen)
|
||
|| !devbuffer(&dvo, (char *)dvi.d_buff, dvi.d_blen)) {
|
||
exit(1);
|
||
}
|
||
|
||
|
||
/* Do it! */
|
||
fprintf(logfile, "; Copying from \"%s\" to \"%s\"...\n", dvi.d_path,
|
||
dvo.d_path ? dvo.d_path : NULLDEV);
|
||
if (!docopy()) {
|
||
fprintf(logfile, "; Stopped unexpectedly.\n");
|
||
ret = FALSE;
|
||
}
|
||
if (!devclose(&dvo)) {
|
||
fprintf(logfile, "; Error closing output.\n");
|
||
ret = FALSE;
|
||
}
|
||
for (d = &dvi; d; d = (d == &dvi) ? &dvo : NULL) {
|
||
if (d->d_istape)
|
||
fprintf(logfile, "; %3s: %ld+%ld errs, %ld files, %ld recs, %"
|
||
VMTAPE_POS_FMT "d bytes\n",
|
||
d->d_pname, d->mta_herr, d->mta_serr,
|
||
d->d_files, d->d_recs, d->d_tloc);
|
||
}
|
||
|
||
fclose(logfile);
|
||
exit(ret ? 0 : 1);
|
||
}
|
||
|
||
|
||
int docopy(void)
|
||
{
|
||
int err, ret = TRUE;
|
||
int done = FALSE;
|
||
|
||
/* If skipping input, do that first */
|
||
if (sw_fileskip) {
|
||
/* Skip files */
|
||
while ((err = devread(&dvi)) >= 0) {
|
||
if (vmt_isateof(&dvi.d_vmt)) { /* Hit tapemark? */
|
||
dvi.d_files++; /* Bump count of files */
|
||
dvi.d_frecs = 0;
|
||
if (dvi.d_files >= sw_fileskip)
|
||
break;
|
||
}
|
||
if (vmt_isateot(&dvi.d_vmt))
|
||
break;
|
||
}
|
||
}
|
||
if (sw_recskip && (err >= 0) && !vmt_isateot(&dvi.d_vmt)) {
|
||
/* Skip records (after skipping files) */
|
||
while ((err = devread(&dvi)) >= 0) {
|
||
if (vmt_framecnt(&dvi.d_vmt) && (dvi.d_frecs >= sw_recskip))
|
||
break;
|
||
if (vmt_isateof(&dvi.d_vmt)) { /* Hit tapemark? */
|
||
dvi.d_files++; /* Bump count of files */
|
||
dvi.d_frecs = 0;
|
||
break;
|
||
}
|
||
if (vmt_isateot(&dvi.d_vmt))
|
||
break;
|
||
}
|
||
}
|
||
if (err >= 0 && !vmt_isateot(&dvi.d_vmt)) while (!done) {
|
||
/* Get a record/tapemark */
|
||
if ((err = devread(&dvi)) < 0)
|
||
break;
|
||
|
||
/* Now copy results to output device */
|
||
if (vmt_framecnt(&dvi.d_vmt)) {
|
||
if (!devwrite(&dvo, dvi.d_buff,
|
||
(rsiz_t)vmt_framecnt(&dvi.d_vmt))) {
|
||
fprintf(logfile, "; Stopped due to output write error: %s\n",
|
||
os_strerror(-1));
|
||
ret = FALSE;
|
||
break;
|
||
}
|
||
if (sw_maxrec && dvo.d_recs >= sw_maxrec)
|
||
done = TRUE; /* Stop after all checks done */
|
||
}
|
||
|
||
#if 0
|
||
/* XXX This needs more thought/work so it isn't always called each time
|
||
an EOF is encountered by devread() of virtual tape.
|
||
*/
|
||
if (err == FALSE)
|
||
devwerr(&dvo, err);
|
||
#endif
|
||
|
||
if (vmt_isateof(&dvi.d_vmt)) { /* Hit tapemark? */
|
||
long ofrecs = dvi.d_frecs; /* Remember # recs in current file */
|
||
int wwon;
|
||
|
||
dvi.d_files++; /* Bump count of files */
|
||
dvi.d_frecs = 0;
|
||
wwon = devweof(&dvo);
|
||
|
||
if (ofrecs == 0 && !sw_peot) { /* Double tapemark? */
|
||
--dvi.d_files; /* Don't count as another file */
|
||
if (wwon) --dvo.d_files;
|
||
break; /* Double tapemark == stop */
|
||
}
|
||
if (sw_maxfile && dvo.d_files >= sw_maxfile)
|
||
break;
|
||
}
|
||
|
||
if (vmt_isateot(&dvi.d_vmt)) /* If input tape has reached EOT, */
|
||
break; /* quit loop, we're done! */
|
||
}
|
||
|
||
if (err < 0) {
|
||
fprintf(logfile, "; Stopped due to input read error: %s\n",
|
||
os_strerror(-1));
|
||
ret = FALSE;
|
||
}
|
||
|
||
/* Copy either done or aborted. Finalize output. This call
|
||
** ensures that all output buffers are flushed and the tape dir, if
|
||
** one, is written out.
|
||
*/
|
||
devweot(&dvo); /* Write "EOT" */
|
||
|
||
return ret;
|
||
}
|
||
|
||
|
||
/* Special test hack.
|
||
** Read in the input tape control file, if one, and output
|
||
** parsed result to stdout.
|
||
** This is used to verify that parsing works.
|
||
*/
|
||
static int
|
||
do_tdtest(void)
|
||
{
|
||
FILE *tdif, *tdof;
|
||
char *errstr = NULL;
|
||
|
||
if (!dvi.d_cpath) {
|
||
fprintf(logfile, "; No input tape directory specified\n");
|
||
return 1;
|
||
}
|
||
if (!dvo.d_cpath) {
|
||
fprintf(logfile, "; No output tape directory specified\n");
|
||
return 1;
|
||
}
|
||
|
||
if (dvi.d_path && dvi.d_istape == MTYP_VIRT) {
|
||
struct vmtattrs v;
|
||
v.vmta_mask = VMTA_PATH | VMTA_FMTREQ;
|
||
strncpy(v.vmta_path, dvi.d_path, sizeof(v.vmta_path));
|
||
v.vmta_fmtreq = dvi.d_vfmt;
|
||
if (!vmt_attrmount(&dvi.d_vmt, &v)) /* Try mounting RO */
|
||
errstr = "vmt error";
|
||
} else { /* Sigh, painful method */
|
||
if (dvi.d_cpath[0] == '\0')
|
||
tdif = stdin;
|
||
else if ((tdif = fopen(dvi.d_cpath, "r")) == NULL) {
|
||
errstr = os_strerror(-1);
|
||
}
|
||
if (!errstr && !tdr_scan(&dvi.d_vmt, tdif, dvi.d_cpath))
|
||
errstr = "parse error";
|
||
}
|
||
|
||
if (errstr) {
|
||
fprintf(logfile,"; Problem with tape desc file \"%s\": %s\n",
|
||
dvi.d_cpath, errstr);
|
||
return 1;
|
||
}
|
||
|
||
/* If here, parse of tape desc file won, so output it. */
|
||
if (strcmp(dvo.d_cpath, "") == 0) {
|
||
tdof = stdout;
|
||
} else if ((tdof = fopen(dvo.d_cpath, "w")) == NULL) {
|
||
fprintf(logfile,"Cannot open tape desc file \"%s\": %s\n",
|
||
dvo.d_cpath, os_strerror(-1));
|
||
return FALSE;
|
||
}
|
||
|
||
td_fout(&dvi.d_vmt.mt_tdr, tdof, "<none>"); /* Just show results */
|
||
return 0;
|
||
}
|
||
|
||
int swerrs = 0;
|
||
|
||
void swerror(char *fmt, ...)
|
||
{
|
||
++swerrs;
|
||
{
|
||
va_list ap;
|
||
va_start(ap, fmt);
|
||
vfprintf(stderr, fmt, ap);
|
||
va_end(ap);
|
||
}
|
||
putc('\n', stderr);
|
||
}
|
||
|
||
|
||
int
|
||
cmdsget(int ac, char **av)
|
||
{
|
||
register char *cp, *arg;
|
||
register int plen;
|
||
struct dev *d;
|
||
long ltmp;
|
||
|
||
dvi.d_istape = dvo.d_istape = MTYP_NULL;
|
||
|
||
while (--ac > 0 && (cp = *++av)) {
|
||
if ((arg = strchr(cp, '='))) /* If arg furnished for param, */
|
||
*arg++ = '\0'; /* split arg off */
|
||
if ((plen = strlen(cp)) <= 0)
|
||
break; /* Bad param */
|
||
|
||
/* Now identify parameter. No case folding for now, sigh */
|
||
if (*cp == 'i' || *cp == 'o') {
|
||
/* Handle {i,o}{t,d,f,b} */
|
||
if (!arg) {
|
||
swerror("Parameter requires arg: \"%s\"", *av);
|
||
continue;
|
||
}
|
||
d = (*cp == 'i') ? &dvi : &dvo;
|
||
switch (*++cp) {
|
||
case 't':
|
||
if (strlen(cp) > 3) break;
|
||
if (d->d_path) {
|
||
swerror("Param already specified: \"%s\"", *av);
|
||
continue;
|
||
}
|
||
d->d_path = arg;
|
||
switch (*++cp) {
|
||
case '\0':
|
||
case 'h': d->d_istape = MTYP_HALF; continue;
|
||
case 'q': d->d_istape = MTYP_QIC; continue;
|
||
case '8': d->d_istape = MTYP_8MM; continue;
|
||
case '4': d->d_istape = MTYP_4MM; continue;
|
||
case 'v': d->d_istape = MTYP_VIRT;
|
||
switch (*++cp) {
|
||
case '\0':
|
||
case 'u': d->d_vfmt = VMT_FMT_UNK; continue;
|
||
case 'r': d->d_vfmt = VMT_FMT_RAW; continue;
|
||
case 'c': d->d_vfmt = VMT_FMT_TPC; continue;
|
||
case 'e': d->d_vfmt = VMT_FMT_TPE; continue;
|
||
case 's': d->d_vfmt = VMT_FMT_TPS; continue;
|
||
#if VMTAPE_ITSDUMP
|
||
case 'i': d->d_vfmt = VMT_FMT_ITS; continue;
|
||
#endif
|
||
#if VMTAPE_T20DUMP
|
||
case '6': d->d_vfmt = VMT_FMT_DUMPER6; continue;
|
||
#endif
|
||
default:
|
||
swerror("Unknown format spec: \"%s\"", cp);
|
||
break; /* Drop thru to fail */
|
||
}
|
||
default:
|
||
swerror("Unknown device spec: \"%s\"", cp);
|
||
break; /* Drop thru to fail */
|
||
}
|
||
break;
|
||
case 'c':
|
||
case 'd':
|
||
if (*++cp) break; /* Ensure param name done */
|
||
if (d->d_cpath) {
|
||
swerror("Param already specified: \"%s\"", *av);
|
||
continue;
|
||
}
|
||
d->d_cpath = arg;
|
||
continue;
|
||
case 'f':
|
||
if (*++cp) break; /* Ensure param name done */
|
||
if (d->d_rpath) {
|
||
swerror("Param already specified: \"%s\"", *av);
|
||
continue;
|
||
}
|
||
d->d_rpath = arg;
|
||
continue;
|
||
case 'b':
|
||
if (*++cp != 's' || *++cp) /* Ensure param name done */
|
||
break;
|
||
if (d->d_recsiz) {
|
||
swerror("Param already specified: \"%s\"", *av);
|
||
continue;
|
||
}
|
||
if (sscanf(arg, "%ld", <mp) != 1)
|
||
swerror("Bad arg to %s: \"%s\"", *av, arg);
|
||
d->d_recsiz = ltmp;
|
||
continue;
|
||
|
||
/* Default just drops thru to fail */
|
||
}
|
||
swerror("Unknown parameter \"%s\"", *av);
|
||
continue;
|
||
}
|
||
switch (plen) {
|
||
case 1:
|
||
if (*cp == 'v') {
|
||
sw_verbose = TRUE;
|
||
continue;
|
||
}
|
||
break;
|
||
case 2:
|
||
if (strcmp(cp, "bs")==0) {
|
||
if (!arg || sscanf(arg, "%ld", <mp) != 1)
|
||
swerror("Bad arg to bs: \"%s\"", arg ? arg : "");
|
||
sw_bothsiz = ltmp;
|
||
continue;
|
||
}
|
||
break;
|
||
case 3:
|
||
if (strcmp(cp, "log")==0) {
|
||
if (!(sw_logpath = arg))
|
||
swerror("Bad arg to log: \"\"");
|
||
continue;
|
||
}
|
||
break;
|
||
case 4:
|
||
if (strcmp(cp, "rcnt")==0) {
|
||
if (!arg || sscanf(arg, "%ld", &sw_maxrec) != 1)
|
||
swerror("Bad arg to rcnt: \"%s\"", arg ? arg : "");
|
||
continue;
|
||
}
|
||
if (strcmp(cp, "fcnt")==0) {
|
||
if (!arg || sscanf(arg, "%ld", &sw_maxfile) != 1) {
|
||
swerror("Bad arg to fcnt: \"%s\"", arg ? arg : "");
|
||
}
|
||
continue;
|
||
}
|
||
if (strcmp(cp, "peot")==0) {
|
||
sw_peot = TRUE;
|
||
continue;
|
||
}
|
||
if (strcmp(cp, "test")==0) {
|
||
sw_tdtest = TRUE;
|
||
continue;
|
||
}
|
||
break;
|
||
case 5:
|
||
if (strcmp(cp, "rskip")==0) {
|
||
if (!arg || sscanf(arg, "%ld", &sw_recskip) != 1)
|
||
swerror("Bad arg to rskip: \"%s\"", arg ? arg : "");
|
||
continue;
|
||
}
|
||
if (strcmp(cp, "fskip")==0) {
|
||
if (!arg || sscanf(arg, "%ld", &sw_fileskip) != 1) {
|
||
swerror("Bad arg to fskip: \"%s\"", arg ? arg : "");
|
||
}
|
||
continue;
|
||
}
|
||
break;
|
||
case 7:
|
||
if (strcmp(cp, "verbose")==0) {
|
||
sw_verbose = TRUE;
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
swerror("Unknown parameter \"%s\"\n%s", *av, nusage);
|
||
return 1;
|
||
}
|
||
|
||
/* Ensure input/output specs make sense. Must have either T or D */
|
||
if (!dvi.d_path) {
|
||
/* Normally an error, but allow it if id= and od= both
|
||
** exist, without an ot= spec -- for testing tapedir scanning.
|
||
*/
|
||
if (dvi.d_cpath && !dvi.d_rpath
|
||
&& !dvo.d_path && dvo.d_cpath && !dvo.d_rpath)
|
||
sw_tdtest = TRUE;
|
||
else
|
||
swerror("Must have it= parameter");
|
||
}
|
||
if (!dvo.d_path && dvo.d_rpath) {
|
||
/* Disallow of= without ot= for now, because type of output
|
||
** device is ambiguous.
|
||
*/
|
||
swerror("Must have ot= to use of=");
|
||
}
|
||
|
||
/* Check for any parameter errors */
|
||
if (swerrs) {
|
||
fprintf(stderr, "%s", nusage);
|
||
return swerrs;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Generic device routines (null, virtual, and real)
|
||
** Open, Close, Read, Write, Write-EOF, Write-EOT.
|
||
*/
|
||
|
||
int devopen(register struct dev *d, int wrtf)
|
||
{
|
||
char *cpath, *rpath;
|
||
|
||
/* Generate default filenames in consistent fashion and open them.
|
||
** See table in comments near switch descriptions.
|
||
*/
|
||
if (d->d_istape == MTYP_VIRT) {
|
||
#if 1 /* New regime */
|
||
struct vmtattrs v;
|
||
|
||
v.vmta_mask = VMTA_FMTREQ | VMTA_MODE | VMTA_PATH | VMTA_UNZIP;
|
||
v.vmta_fmtreq = d->d_vfmt;
|
||
v.vmta_mode = wrtf ? VMT_MODE_CREATE : VMT_MODE_RDONLY;
|
||
v.vmta_unzip = TRUE;
|
||
strncpy(v.vmta_path, d->d_path, sizeof(v.vmta_path));
|
||
if (d->d_cpath) {
|
||
v.vmta_mask |= VMTA_CTLPATH;
|
||
strncpy(v.vmta_ctlpath, d->d_cpath, sizeof(v.vmta_ctlpath));
|
||
}
|
||
if (d->d_rpath) {
|
||
v.vmta_mask |= VMTA_DATPATH;
|
||
strncpy(v.vmta_datpath, d->d_rpath, sizeof(v.vmta_datpath));
|
||
}
|
||
if (!vmt_attrmount(&d->d_vmt, &v)) { /* Try mounting it */
|
||
fprintf(logfile, "; Couldn't mount %sput virtual tape: %s\n",
|
||
d->d_pname, d->d_path);
|
||
return FALSE;
|
||
}
|
||
|
||
#else /* Old regime */
|
||
/* Use path as cpath; generate rawdata filename */
|
||
if (!(cpath = d->d_cpath)) {
|
||
/* Default cpath to path */
|
||
cpath = d->d_path;
|
||
}
|
||
if (!(rpath = d->d_rpath)) {
|
||
/* Default rpath to path.tap, unless path ends with .tdr
|
||
** in which case replace the .tdr with .tap.
|
||
*/
|
||
char *s;
|
||
rpath = malloc(strlen(d->d_path)+4+1);
|
||
if (!rpath) {
|
||
fprintf(logfile, "; Cannot malloc data filename\n");
|
||
return FALSE;
|
||
}
|
||
strcpy(rpath, d->d_path);
|
||
if (!((s = strrchr(rpath, '.')) && (strcmp(s, ".tdr")==0)))
|
||
s = rpath + strlen(rpath);
|
||
strcpy(s, ".tap");
|
||
}
|
||
|
||
/* Defaults all set up, store them back in structure */
|
||
d->d_cpath = cpath;
|
||
d->d_rpath = rpath;
|
||
|
||
/* Open up virtual files. Need special hackery if either
|
||
** the tapedesc or rawdata path was explicitly given.
|
||
*/
|
||
if (!vmt_xmount(&(d->d_vmt), d->d_cpath, d->d_rpath, wrtf)) {
|
||
fprintf(logfile, "; Couldn't mount %sput virtual tape: %s\n",
|
||
d->d_pname, d->d_path);
|
||
return FALSE;
|
||
}
|
||
#endif
|
||
|
||
|
||
} else {
|
||
/* Default filenames for real device */
|
||
cpath = d->d_cpath; /* Desc file may not be wanted */
|
||
if (!(rpath = d->d_rpath))
|
||
rpath = d->d_path; /* Data file defaults to tapefile */
|
||
|
||
/* Defaults all set up, store them back in structure */
|
||
d->d_cpath = cpath;
|
||
d->d_rpath = rpath;
|
||
|
||
if (d->d_rpath) {
|
||
/* Open real tape device */
|
||
if (strcmp(d->d_rpath, "") == 0) {
|
||
d->d_fd = wrtf ? FD_STDOUT : FD_STDIN;
|
||
} else if (!os_mtopen(d, wrtf)) {
|
||
fprintf(logfile, "; Cannot open %sput: %s\n",
|
||
d->d_pname, os_strerror(-1));
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
/* Set up VMT vars, with optional tape-info if specified.
|
||
** Note virtual tape isn't set here; vmt_mount already did that.
|
||
*/
|
||
switch (d->d_istape) {
|
||
case MTYP_NULL: d->d_vmt.mt_devname = "NUL"; break;
|
||
case MTYP_HALF: d->d_vmt.mt_devname = "MTA"; break;
|
||
case MTYP_QIC: d->d_vmt.mt_devname = "QIC"; break;
|
||
case MTYP_8MM: d->d_vmt.mt_devname = "8MM"; break;
|
||
case MTYP_4MM: d->d_vmt.mt_devname = "4MM"; break;
|
||
}
|
||
|
||
|
||
/* See if optional tapedesc for non-virtual drive. If so,
|
||
** fake out virtual tape code to think it has a tapedesc.
|
||
** This is ugly and fragile.
|
||
*/
|
||
if (d->d_cpath) {
|
||
|
||
if (!(d->d_vmt.mt_filename = dupcstr(d->d_cpath))) {
|
||
fprintf(logfile, "; malloc failed for cpath dupcstr\n");
|
||
return FALSE;
|
||
}
|
||
if (strcmp(d->d_cpath, "") == 0) {
|
||
d->d_vmt.mt_ctlf = (wrtf ? stdout : stdin);
|
||
} else if ((d->d_vmt.mt_ctlf =
|
||
fopen(d->d_cpath, (wrtf ? "w" : "r"))) == NULL) {
|
||
fprintf(logfile,"Cannot open tape desc file \"%s\": %s\n",
|
||
d->d_cpath, os_strerror(-1));
|
||
return FALSE;
|
||
}
|
||
|
||
/* Tape-desc file open, now parse it if reading */
|
||
if (!wrtf && !tdr_scan(&(d->d_vmt), d->d_vmt.mt_ctlf, d->d_cpath)) {
|
||
fprintf(logfile, "; Cannot parse tape desc\n");
|
||
return FALSE;
|
||
}
|
||
/* Do final setup if writing */
|
||
if (wrtf && d->d_rpath) {
|
||
if (!(d->d_vmt.mt_datpath = dupcstr(d->d_rpath))) {
|
||
fprintf(logfile, "; malloc failed for rpath dupcstr\n");
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/* Set default buffer length */
|
||
if ((d->d_blen = d->d_vmt.mt_tdr.tdmaxrsiz)) {
|
||
if (d->d_recsiz) { /* Explicit record size spec? */
|
||
if (d->d_blen <= d->d_recsiz) /* If it's bigger, */
|
||
d->d_blen = d->d_recsiz; /* adjust quietly */
|
||
else /* else warn user */
|
||
fprintf(logfile, "; Tapedir forcing larger %sput record size (%ld instead of %ld)\n",
|
||
d->d_pname, (long)d->d_blen, (long)d->d_recsiz);
|
||
|
||
}
|
||
} else /* No tapedir, use spec if any */
|
||
d->d_blen = d->d_recsiz ? d->d_recsiz : MAXRECSIZE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Set up device buffering.
|
||
** Must be called after device already opened.
|
||
** Probably will lose if called after I/O done.
|
||
** If buffp set, will use that with blen and not free it when closed.
|
||
** If buffp NULL, will allocate buffer of size blen, unless blen is 0
|
||
** in which case it figures out a default.
|
||
*/
|
||
int devbuffer(struct dev *d, char *buffp, rsiz_t blen)
|
||
{
|
||
if (blen <= 0) {
|
||
blen = MAXRECSIZE; /* No size spec, use a safe default */
|
||
buffp = NULL; /* Ensure any pointer arg is ignored */
|
||
}
|
||
|
||
d->d_bisdyn = FALSE;
|
||
if (buffp == NULL) {
|
||
/* Always round actual buffer size upwards to be modulo-512 in case
|
||
** either input or output has to deal with QIC drives, otherwise the
|
||
** unix I/O call will fail without even trying to do anything!
|
||
*/
|
||
blen = (blen + 511) & ~511; /* Relies on fact 512 is power of 2 */
|
||
if ((buffp = malloc(blen)) == NULL) {
|
||
fprintf(logfile, "; Cannot malloc %sput buffer (size %ld)\n",
|
||
d->d_pname, blen);
|
||
return FALSE;
|
||
}
|
||
d->d_bisdyn = TRUE;
|
||
}
|
||
d->d_blen = blen;
|
||
d->d_buff = (char8 *)buffp;
|
||
d->d_iop = d->d_buff;
|
||
d->d_buse = 0;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
int devclose(struct dev *d)
|
||
{
|
||
int ret;
|
||
|
||
if (d->d_istape == MTYP_VIRT) {
|
||
ret = vmt_unmount(&d->d_vmt); /* Close virtual tape */
|
||
} else {
|
||
ret = TRUE;
|
||
if (d->d_rpath)
|
||
ret = os_mtclose(d); /* Close real tape */
|
||
if (d->d_cpath) {
|
||
struct vmtape *t = &d->d_vmt; /* Close tapedesc */
|
||
|
||
rewind(t->mt_ctlf); /* Rewind in case not the first update */
|
||
td_fout(&t->mt_tdr, t->mt_ctlf, t->mt_datpath);
|
||
fflush(t->mt_ctlf);
|
||
if (ferror(t->mt_ctlf)) /* Check to see if any errors */
|
||
ret = FALSE;
|
||
}
|
||
}
|
||
|
||
if (d->d_bisdyn && d->d_buff) {
|
||
free((char *)(d->d_buff));
|
||
d->d_buff = NULL;
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
|
||
/* Read one data unit (record, tapemark, etc) from device
|
||
** Returns 1 if read something
|
||
** Returns 0 if read nothing, but no error
|
||
** Returns -1 if error of some kind
|
||
*/
|
||
|
||
int devread(struct dev *d)
|
||
{
|
||
int ret;
|
||
|
||
if (d->d_istape == MTYP_VIRT) {
|
||
/* Virtual tape, read in and change vmt params directly */
|
||
ret = vmt_rget(&(d->d_vmt), d->d_buff, (size_t)d->d_blen);
|
||
} else if (d->d_rpath) {
|
||
/* Real tape, attempt physical read */
|
||
|
||
/* Still to do? Handle dir-only case, plus dir+dev case? */
|
||
ret = os_mtread(d); /* Attempt read from device */
|
||
if (ret < 0) {
|
||
fprintf(logfile, "; %sput device read error: %s\n",
|
||
d->d_pname, os_strerror(-1));
|
||
return ret;
|
||
}
|
||
|
||
if ((d->d_vmt.mt_frames = d->mta_frms)) { /* Read any data? */
|
||
d->d_buse = d->mta_frms; /* Say this much of buffer used */
|
||
d->d_iop = d->d_buff;
|
||
}
|
||
d->d_vmt.mt_eof = d->mta_eof;
|
||
d->d_vmt.mt_eot = d->mta_eot;
|
||
d->d_vmt.mt_bot = d->mta_bot;
|
||
|
||
} else {
|
||
/* Null device, read nothing */
|
||
d->d_vmt.mt_frames = 0; /* No data read */
|
||
d->d_vmt.mt_eof = TRUE; /* At BOF, EOF, and EOT! */
|
||
d->d_vmt.mt_eot = TRUE;
|
||
d->d_vmt.mt_bot = TRUE;
|
||
ret = TRUE;
|
||
}
|
||
|
||
|
||
if (d->d_vmt.mt_frames) {
|
||
d->d_tloc += d->d_vmt.mt_frames;
|
||
d->d_recs++; /* Got a record */
|
||
d->d_frecs++; /* Bump rec cnt for current file */
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
/* Write one record to device.
|
||
** For now, note mtawrite assumes d_buff and d_buse are args.
|
||
*/
|
||
int devwrite(struct dev *d, unsigned char *buff, rsiz_t len)
|
||
{
|
||
int ret = TRUE;
|
||
|
||
if (len) {
|
||
if (d->d_istape == MTYP_VIRT) {
|
||
if ((ret = vmt_rput(&(d->d_vmt), buff, (size_t)len))) {
|
||
d->d_tloc += len;
|
||
d->d_recs++;
|
||
d->d_frecs++;
|
||
}
|
||
} else {
|
||
if (d->d_cpath) /* Add to tapedir if one */
|
||
td_recapp(&(d->d_vmt.mt_tdr), len, 1, 0);
|
||
if (d->d_rpath) { /* Output to real device if one */
|
||
d->d_recsiz = d->d_buse = len;
|
||
ret = os_mtwrite(d);
|
||
if (d->mta_frms) {
|
||
d->d_tloc += d->mta_frms;
|
||
d->d_recs++;
|
||
d->d_frecs++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
int devwerr(struct dev *d, int err)
|
||
{
|
||
if (d->d_istape == MTYP_VIRT)
|
||
return vmt_eput(&(d->d_vmt), err);
|
||
|
||
if (d->d_cpath) { /* Have tapedir? */
|
||
if (!td_recapp(&d->d_vmt.mt_tdr, (long)0, 0, err)) {
|
||
fprintf(logfile, "devwerr: td_recapp malloc failed");
|
||
return FALSE;
|
||
}
|
||
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
int devweof(struct dev *d)
|
||
{
|
||
int ret = TRUE;
|
||
|
||
if (d->d_istape == MTYP_VIRT)
|
||
ret = vmt_eof(&(d->d_vmt));
|
||
else {
|
||
if (d->d_cpath) { /* Have tapedir? */
|
||
if (!td_recapp(&d->d_vmt.mt_tdr, (long)0, 1, 0)) {
|
||
fprintf(logfile, "devweof: td_recapp malloc failed");
|
||
ret = FALSE;
|
||
}
|
||
}
|
||
if (d->d_rpath)
|
||
ret = os_mtweof(d) ? ret : FALSE; /* Keep F if already F */
|
||
}
|
||
if (ret) {
|
||
d->d_files++; /* Bump count of files written */
|
||
d->d_frecs = 0;
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
int devweot(struct dev *d)
|
||
{
|
||
if (d->d_istape == MTYP_VIRT)
|
||
return vmt_eot(&(d->d_vmt));
|
||
|
||
/* Otherwise devclose() will do all that's needed */
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Magtape handling routines
|
||
*/
|
||
|
||
#if CENV_SYS_T20
|
||
|
||
/* MAGTAPE DEVICE STATUS BITS */
|
||
|
||
#define MT_ILW monsym("MT%ILW") /* ILLEGAL WRITE */
|
||
#define MT_DVE monsym("MT%DVE") /* DEVICE ERROR */
|
||
#define MT_DAE monsym("MT%DAE") /* DATA ERROR */
|
||
#define MT_SER monsym("MT%SER") /* SUPPRESS ERROR RECOVERY PROCEDURES */
|
||
#define MT_EOF monsym("MT%EOF") /* EOF (FILE MARK) */
|
||
#define MT_IRL monsym("MT%IRL") /* INCORRECT RECORD LENGTH */
|
||
#define MT_BOT monsym("MT%BOT") /* BEGINNING OF TAPE */
|
||
#define MT_EOT monsym("MT%EOT") /* END OF TAPE */
|
||
#define MT_EVP monsym("MT%EVP") /* EVEN PARITY */
|
||
#define MT_DEN monsym("MT%DEN") /* DENSITY (0 IS 'NORMAL') */
|
||
#define MT_CCT monsym("MT%CCT") /* CHARACTER COUNTER */
|
||
#define MT_NSH monsym("MT%NSH") /* DATA MODE OR DENS NOT SUPPORTED BY HDW */
|
||
|
||
static int acs[5]; /* For any JSYS that needs it */
|
||
|
||
#endif /* CENV_SYS_T20 */
|
||
|
||
int os_mtopen(struct dev *dp, int wrtf)
|
||
{
|
||
int fd;
|
||
|
||
#if CENV_SYS_T20
|
||
acs[1] = monsym("GJ%NEW")|monsym("GJ%SHT");
|
||
acs[2] = (int)(dp->d_path-1);
|
||
if (jsys(GTJFN, acs) <= 0)
|
||
return FALSE;
|
||
fd = (acs[1] &= RH);
|
||
acs[2] = FLD(monsym(".GSDMP"),monsym("OF%MOD"))
|
||
| monsym("OF%RD");
|
||
if (jsys(OPENF, acs) <= 0) {
|
||
acs[1] = fd;
|
||
jsys(RLJFN, acs);
|
||
return FALSE;
|
||
}
|
||
#else
|
||
fd = open(dp->d_path, (wrtf ? O_RDWR : O_RDONLY)|O_BSIZE_8, 0600);
|
||
if (fd < 0)
|
||
return FALSE;
|
||
#endif
|
||
dp->mta_retry = 5; /* Default for now */
|
||
dp->d_fd = fd;
|
||
return TRUE;
|
||
}
|
||
|
||
int os_mtclose(struct dev *dp)
|
||
{
|
||
#if CENV_SYS_T20
|
||
#else
|
||
return close(dp->d_fd);
|
||
#endif
|
||
}
|
||
|
||
int os_mtread(struct dev *dp)
|
||
{
|
||
int retry = dp->mta_retry;
|
||
#if CENV_SYS_T20
|
||
|
||
static int iov[2] = {0, 0};
|
||
int flgs;
|
||
|
||
dp->mta_frms = dp->mta_eof = 0;
|
||
dp->mta_bot = dp->mta_eot = 0;
|
||
for (; retry >= 0; --retry) {
|
||
iov[0] = XWD(-(dp->d_blen/sizeof(int)),((int)(int *)(dp->d_buff))-1);
|
||
acs[1] = dp->d_fd;
|
||
acs[2] = (int)&iov;
|
||
if (jsys(DUMPI, acs) > 0) {
|
||
fprintf(logfile, "; Warning: DUMPI won for max rec size %ld!\n",
|
||
(long)dp->d_blen);
|
||
dp->mta_frms = dp->d_blen;
|
||
return 1;
|
||
}
|
||
/* Error return is normal case, since record size varies */
|
||
switch (acs[1]) {
|
||
case monsym("IOX4"): /* EOF (tape mark) */
|
||
case monsym("IOX5"): /* Device or data error (rec length) */
|
||
acs[1] = dp->d_fd;
|
||
if (jsys(GDSTS, acs) <= 0) {
|
||
fprintf(logfile, "; Tape GDSTS%% error: %s\n", os_strerror(-1));
|
||
os_mtclrerr(dp->d_fd);
|
||
dp->mta_herr++;
|
||
return 0;
|
||
}
|
||
break;
|
||
default:
|
||
fprintf(logfile, "; Tape DUMPI%% error: %s\n", os_strerror(-1));
|
||
os_mtclrerr(dp->d_fd);
|
||
dp->mta_herr++;
|
||
return 0;
|
||
}
|
||
|
||
/* Analyze "error". acs[2] has flags, [3] has count in LH */
|
||
flgs = acs[2];
|
||
t20status(dp, logfile, flgs, acs[3]);
|
||
dp->mta_frms = ((unsigned)acs[3]) >> 18; /* Find cnt of data */
|
||
|
||
/* Opening in industry-compatible mode apparently works to force use of
|
||
** frame count, not word count */
|
||
/* dp->mta_frms *= sizeof(int); */
|
||
os_mtclrerr(dp->d_fd);
|
||
if (flgs & (MT_DVE|MT_DAE)) {
|
||
fprintf(logfile, "; Tape error: %s\n",
|
||
(flgs & MT_DVE) ? "Device" : "Data");
|
||
dp->mta_serr++;
|
||
continue; /* Try again */
|
||
}
|
||
if (flgs & MT_EOF)
|
||
dp->mta_eof = TRUE;
|
||
if (flgs & MT_EOT)
|
||
dp->mta_eot = TRUE;
|
||
if (flgs & MT_BOT)
|
||
dp->mta_bot = TRUE;
|
||
return 1;
|
||
}
|
||
dp->mta_herr++;
|
||
|
||
#else /* !CENV_SYS_T20 */
|
||
|
||
dp->mta_frms = dp->mta_eof = 0;
|
||
dp->mta_bot = dp->mta_eot = 0;
|
||
for (; retry >= 0; --retry) {
|
||
switch (dp->mta_frms = read(dp->d_fd, dp->d_buff, dp->d_blen)) {
|
||
case 0: /* Tapemark */
|
||
dp->mta_eof = TRUE;
|
||
return TRUE;
|
||
|
||
default: /* Assume record read */
|
||
if (dp->mta_frms <= 0) {
|
||
case -1: /* Error */
|
||
dp->mta_serr++;
|
||
fprintf(logfile, "; Tape read error: %s\n", os_strerror(-1));
|
||
os_mtstatus(dp, logfile); /* Show full status */
|
||
if (retry <= 0) {
|
||
if (os_mtfsr(dp)) { /* Try spacing over 1 rec */
|
||
retry = dp->mta_retry;
|
||
dp->mta_herr++;
|
||
} else {
|
||
fprintf(logfile, "; Cannot proceed past read error, aborting...\n");
|
||
return -1;
|
||
}
|
||
}
|
||
continue; /* Try again */
|
||
}
|
||
|
||
/* Special hack for QIC -- pretend we read only the record size
|
||
** specified, although actual read was greater in order to
|
||
** keep I/O using modulo-512 counts.
|
||
*/
|
||
if (dp->d_istape == MTYP_QIC) {
|
||
if (dp->mta_frms > dp->d_recsiz) {
|
||
/* Adjust so devread() gets true tapeloc */
|
||
dp->d_tloc += dp->mta_frms - dp->d_recsiz;
|
||
dp->mta_frms = dp->d_recsiz;
|
||
}
|
||
} else
|
||
if (dp->mta_frms >= dp->d_blen)
|
||
fprintf(logfile, "; Warning: read max rec size %ld!\n",
|
||
dp->mta_frms);
|
||
|
||
|
||
return TRUE;
|
||
}
|
||
}
|
||
dp->mta_frms = 0;
|
||
#endif
|
||
|
||
return 0; /* For now */
|
||
}
|
||
|
||
int os_mtwrite(struct dev *dp)
|
||
{
|
||
int retry = dp->mta_retry;
|
||
#if CENV_SYS_T20
|
||
static int iov[2] = {0, 0};
|
||
int flgs;
|
||
|
||
dp->mta_frms = dp->mta_eof = 0;
|
||
dp->mta_bot = dp->mta_eot = 0;
|
||
for (; retry >= 0; --retry) {
|
||
iov[0] = XWD(-(dp->d_buse/sizeof(int)),((int)(int *)(dp->d_buff))-1);
|
||
acs[1] = dp->d_fd;
|
||
acs[2] = (int)&iov;
|
||
if (jsys(DUMPO, acs) > 0) {
|
||
dp->mta_frms = dp->d_buse;
|
||
return 1;
|
||
}
|
||
/* Error return */
|
||
dp->mta_serr++;
|
||
fprintf(logfile, "; Tape DUMPO%% error: %s\n", os_strerror(-1));
|
||
os_mtclrerr(dp->d_fd);
|
||
dp->mta_herr++;
|
||
return 0;
|
||
}
|
||
#elif CENV_SYS_UNIX /* !CENV_SYS_T20 */
|
||
char8 *iop = dp->d_buff;
|
||
int ret;
|
||
rsiz_t full = dp->d_buse;
|
||
rsiz_t used = full;
|
||
|
||
dp->mta_frms = 0;
|
||
|
||
/* Special hack for QIC -- must bump up length to modulo-512.
|
||
*/
|
||
if (dp->d_istape == MTYP_QIC) {
|
||
full = (used + 0777) & ~0777; /* Round up to 512-byte boundary */
|
||
if (used < dp->d_recsiz)
|
||
fprintf(logfile, "; Warning: partial record padded out (%ld => %ld)\n",
|
||
(long)used, (long)full);
|
||
if (full > dp->d_blen) {
|
||
fprintf(logfile, "; Internal bug: padout exceeds buffer (%ld > %ld)\n",
|
||
(long)full, (long)dp->d_blen);
|
||
full = dp->d_blen;
|
||
}
|
||
if (full > used) {
|
||
memset(dp->d_buff + used, 0, full - used); /* Zap padding */
|
||
dp->d_tloc += full - used; /* Add extra so devwrite */
|
||
} /* knows true tapeloc */
|
||
used = full;
|
||
}
|
||
|
||
|
||
do {
|
||
if ((ret = write(dp->d_fd, iop, used)) == used) {
|
||
dp->mta_frms += used; /* Won! */
|
||
return 1;
|
||
}
|
||
/* Error of some kind */
|
||
dp->mta_serr++;
|
||
fprintf(logfile, "; (Rec %ld, try %d) ",
|
||
dp->d_recs, dp->mta_retry - retry);
|
||
if (ret < 0) {
|
||
fprintf(logfile, "Tape write error: %s\n", os_strerror(-1));
|
||
} else {
|
||
fprintf(logfile, "Tape write truncated: %d, shd be %ld\n",
|
||
ret, (long)used);
|
||
if (ret > 0) {
|
||
used -= ret; /* Update to write out rest */
|
||
iop += ret;
|
||
dp->d_recs++; /* Sigh, stupid crock here */
|
||
dp->d_frecs++;
|
||
dp->mta_frms += ret;
|
||
}
|
||
}
|
||
os_mtstatus(dp, logfile); /* Show full status */
|
||
} while (--retry > 0); /* Continue N times */
|
||
dp->mta_herr++;
|
||
#endif
|
||
|
||
return 0; /* For now */
|
||
}
|
||
|
||
int os_mtweof(struct dev *dp) /* Write a tapemark */
|
||
{
|
||
#if CENV_SYS_T20
|
||
acs[1] = dp->d_fd;
|
||
acs[2] = monsym(".MOCLE");
|
||
jsys(MTOPR, acs);
|
||
#elif CENV_SYS_UNIX && !HAVE_SYS_MTIO_H
|
||
return FALSE;
|
||
#else
|
||
struct mtop mtcmd;
|
||
mtcmd.mt_op = MTWEOF;
|
||
mtcmd.mt_count = 1;
|
||
if (ioctl(dp->d_fd, MTIOCTOP, &mtcmd) < 0) {
|
||
fprintf(logfile, "; MTWEOF ioctl failed: %s\n", os_strerror(-1));
|
||
dp->mta_herr++;
|
||
return FALSE;
|
||
}
|
||
#endif
|
||
return TRUE;
|
||
}
|
||
|
||
int os_mtfsr(struct dev *dp) /* Forward Space Record (to inter-record gap)*/
|
||
{
|
||
#if CENV_SYS_T20
|
||
/* acs[1] = dp->d_fd;
|
||
acs[2] = monsym(".MOCLE");
|
||
jsys(MTOPR, acs);
|
||
*/
|
||
#elif CENV_SYS_UNIX && !HAVE_SYS_MTIO_H
|
||
return FALSE;
|
||
#else
|
||
struct mtop mtcmd;
|
||
mtcmd.mt_op = MTFSR;
|
||
mtcmd.mt_count = 1;
|
||
if (ioctl(dp->d_fd, MTIOCTOP, &mtcmd) < 0) {
|
||
fprintf(logfile, "; MTFSR ioctl failed: %s\n", os_strerror(-1));
|
||
dp->mta_herr++;
|
||
return FALSE;
|
||
}
|
||
#endif
|
||
return TRUE;
|
||
}
|
||
|
||
void os_mtclrerr(int fd)
|
||
{
|
||
#if CENV_SYS_T20
|
||
acs[1] = fd;
|
||
acs[2] = monsym(".MOCLE");
|
||
jsys(MTOPR, acs);
|
||
#endif /* CENV_SYS_T20 */
|
||
}
|
||
|
||
#if CENV_SYS_T20
|
||
void
|
||
t20status(register struct dev *d, register FILE *f, int swd, int cnt)
|
||
{
|
||
fprintf(f, "; Tape status: Cnt %d", ((unsigned)cnt)>>18);
|
||
if (cnt & RH) fprintf(f, ",,%d", cnt & RH);
|
||
fprintf(f, " Flgs: %#o", swd&RH);
|
||
|
||
if (swd & MT_ILW) fprintf(f, " ILW"); /* ILLEGAL WRITE */
|
||
if (swd & MT_DVE) fprintf(f, " DVE"); /* DEVICE ERROR */
|
||
if (swd & MT_DAE) fprintf(f, " DAE"); /* DATA ERROR */
|
||
if (swd & MT_SER) fprintf(f, " SER"); /* SUPPRESS ERROR RECOVERY PROCS */
|
||
if (swd & MT_EOF) fprintf(f, " EOF"); /* EOF (FILE MARK) */
|
||
if (swd & MT_IRL) fprintf(f, " IRL"); /* INCORRECT RECORD LENGTH */
|
||
if (swd & MT_BOT) fprintf(f, " BOT"); /* BEGINNING OF TAPE */
|
||
if (swd & MT_EOT) fprintf(f, " EOT"); /* END OF TAPE */
|
||
if (swd & MT_EVP) fprintf(f, " EVP"); /* EVEN PARITY */
|
||
if (swd & MT_DEN) fprintf(f, " DEN=%d", /* DENSITY (0 IS 'NORMAL') */
|
||
FLDGET(swd, MT_DEN));
|
||
if (swd & MT_CCT) fprintf(f, " CCT=%d", /* CHARACTER COUNTER */
|
||
FLDGET(swd, MT_CCT));
|
||
if (swd & MT_NSH) fprintf(f, " NSH"); /* DATA MODE OR DENS NOT SUPPORTED */
|
||
fprintf(f, "\n");
|
||
}
|
||
#endif /* CENV_SYS_T20 */
|
||
|
||
void os_mtstatus(struct dev *dp, FILE *f)
|
||
{
|
||
#if CENV_SYS_T20
|
||
#elif CENV_SYS_UNIX && HAVE_SYS_MTIO_H
|
||
struct mtget mtstatb;
|
||
|
||
if (ioctl(dp->d_fd, MTIOCGET, (char *)&mtstatb) < 0) {
|
||
fprintf(f, "; Couldn't get status for %s; %s\n",
|
||
dp->d_path, os_strerror(-1));
|
||
return;
|
||
}
|
||
fprintf(f, "; Status for magtape %s:\n", dp->d_path);
|
||
fprintf(f, "; Type: %#x (vals in sys/mtio.h)\n", (int)mtstatb.mt_type);
|
||
# if CENV_SYS_SUN
|
||
fprintf(f, "; Flags: %#o ->", mtstatb.mt_flags);
|
||
if (mtstatb.mt_flags & MTF_SCSI) fprintf(f, " SCSI");
|
||
if (mtstatb.mt_flags & MTF_REEL) fprintf(f, " REEL");
|
||
if (mtstatb.mt_flags & MTF_ASF) fprintf(f, " ABSFILPOS");
|
||
fprintf(f, "\n");
|
||
fprintf(f, "; Optim blkfact: %d\n", mtstatb.mt_bf);
|
||
# endif
|
||
|
||
fprintf(f, "; Drive status: %#o (dev dep)\n", (int)mtstatb.mt_dsreg);
|
||
fprintf(f, "; Error status: %#o (dev dep)\n", (int)mtstatb.mt_erreg);
|
||
fprintf(f, "; Err - Cnt left: %ld\n", (long)mtstatb.mt_resid);
|
||
# if CENV_SYS_SUN
|
||
fprintf(f, "; Err - File num: %ld\n", (long)mtstatb.mt_fileno);
|
||
fprintf(f, "; Err - Rec num: %ld\n", (long)mtstatb.mt_blkno);
|
||
# endif
|
||
|
||
#endif
|
||
}
|