diff options
author | Joseph Hunkeler <jhunkeler@gmail.com> | 2015-07-08 20:46:52 -0400 |
---|---|---|
committer | Joseph Hunkeler <jhunkeler@gmail.com> | 2015-07-08 20:46:52 -0400 |
commit | fa080de7afc95aa1c19a6e6fc0e0708ced2eadc4 (patch) | |
tree | bdda434976bc09c864f2e4fa6f16ba1952b1e555 /sys/mtio | |
download | iraf-linux-fa080de7afc95aa1c19a6e6fc0e0708ced2eadc4.tar.gz |
Initial commit
Diffstat (limited to 'sys/mtio')
35 files changed, 3601 insertions, 0 deletions
diff --git a/sys/mtio/README b/sys/mtio/README new file mode 100644 index 00000000..143917e6 --- /dev/null +++ b/sys/mtio/README @@ -0,0 +1,45 @@ +MTIO -- Magtape i/o. This directory contains the FIO device driver for the +magtape devices plus MTOPEN (used to open a magtape device and hook it to +FIO) plus a few other routines. See ./doc for additional info. + + +USER ROUTINES + + yes|no = mtfile (fname) # Is fname a magtape device? + yes|no = mtneedfileno (mtname) # No file number given? + gty = mtcap (mtname) + mtfname (mtname, file, outstr, maxch) + + mtparse (mtname, device, fileno, recno, attrl, maxch) + mtencode (mtname, maxch, device, fileno, recno, attrl) + + fd = mtopen (mtname, acmode, bufsize) + mtrewind (mtname, initcache) + mtposition (mtname, file, record) + + Use CLOSE to close a file descriptor opened with mtopen. + Use GTYCLOSE to close a termcap descriptor opened with mtcap. + MTNAME is the full magtape device specification, i.e, "mt[...]". + + + +SYSTEM ROUTINES + + mtallocate (mtname) + mtdeallocate (mtname, rewind_tape) + mtstatus (out, mtname) + mtclean (level, stale, out) + + + +FIO DRIVER + + zopnmt (iodev, acmode, mtchan) + zclsmt (mtchan, status) + zardmt (mtchan, buf, maxbytes, offset) + zawrmt (mtchan, buf, nbytes, offset) + zawtmt (mtchan, status) + zsttmt (mtchan, what, lvalue) + + +All other procedures are internal to the interface. diff --git a/sys/mtio/doc/mtio.hlp b/sys/mtio/doc/mtio.hlp new file mode 100644 index 00000000..e00643a5 --- /dev/null +++ b/sys/mtio/doc/mtio.hlp @@ -0,0 +1,814 @@ +.help mtio Aug83 "Magnetic Tape I/O" +.sh +1. Introduction + + This document describes the Magnetic Tape I/O (MTIO) package, +i.e., the interface by which IRAF programs access magnetic tapes. +Included are the requirements and specifications for the package, +and a discussion of the interface between MTIO and FIO. + +.sh +2. MTIO Requirements +.ls 4 +.ls (1) +The machine independent part of the MTIO package shall be written in +the SPP language in compliance with the standards and conventions of IRAF. +.le +.ls (2) +The MTIO package shall provide a single interface to all magtape +devices on all IRAF target machines. +.le +.ls (3) +MTIO shall interface to FIO, making it possible to read and write a magtape +file via the FIO interface in a device independent fashion. +.le +.ls (4) +All functions shall take an error action if a hardware error occurs, +or if an illegal function is requested. +No hardware exception or trap shall occur while accessing a magtape +file via MTIO which is not caught by MTIO and converted into an +IRAF error action. +.le +.ls (5) +All error actions shall be defined symbolically in the system error +code file <syserr.h>. The MTIO error messages shall have the +form SYS_MT<function>, i.e., SYS_MTOPEN. The error message strings +shall be placed in the system error message file, "syserrmsg". +.le +.ls (6) +The MTIO code shall be divided into a machine independent part and +a machine dependent part. All machine independent function and error +checking shall be done in the machine independent part, to minimize +the size and complexity of the machine dependent part, and therefore +maximize the transportability of the package. +.le +.ls (7) +MTIO shall interface to FIO by implementing the standard FIO binary file +z-routines ZOPNMT, ZCLSMT, ZARDMT, ZAWRMT, ZAWTMT, and ZSTTMT. +The specifications for these routines are given elsewhere. +.le + +.sh +3. MTIO Specifications + + A magtape file is opened with MTOPEN and then accessed as a binary +file via the device independent FIO interface. Upon input, FIO merges +all tape records together into a binary byte stream. Upon output, +FIO writes records equal in size to the FIO buffers, unless FLUSH or +CLOSE is called, causing a partially filled buffer to be flushed to tape. +The data within a tape file may not be randomly accessed; only sequential +accesses are permitted. When i/o is complete, CLOSE should be called +to close the file and return all buffer space. + + fd = mtopen (filename, access_mode, buf_size) + +The format of a nonblank magnetic tape consists of beginning-of-tape (BOT), +followed by zero or more files, followed by end-of-tape (EOT). +Each file consists of one or more records, followed by a tape mark (EOF). +EOT is defined as two consecutive tape marks, i.e., as a file containing +zero records. MTIO knows nothing about labeled tapes. + +Tape records need not all be the same length, but for transportability +reasons records should be an integral number of computer words in length, +and very short records should be avoided (short records are used by some +operating systems for special purposes). The term "computer word" +is not well defined; the size of a tape record in bytes should be evenly +divisible by 2, 4, or 8, where the transportability increases with the +size of the divisor. To avoid loss of data when reading a tape, +the FIO buffer must be at least as large as the longest record on the tape. + +.sh +3.1 Opening a Magtape + + MTOPEN opens a file on the named magtape device. The following +device naming convention determines the tape drive and density: + + mt[a-z][800|1600|6250] + +The letter in the second field determines which drive is to be used, +and the third field, which is optional, determines the density. +If the density is not specified, a system or drive dependent default +density will be selected. Thus, to access a file on drive A at 1600 bpi, +one would open the file "mta1600". + +The significance of the terms "drive A", "drive B", etc., is installation +dependent, and need not imply distinct physical drives. A tape drive may +have to be allocated before it can be opened by MTIO; MTIO does not attempt +to allocate devices. + +The device name may optionally be followed by a subscript specifying +the index of the file to which the tape is to be positioned. +Thus, opening "mta[3]" causes device A to be opened with the default +density, positioned to BOF of the third file on the tape. +The field N, as in "mta[N]" may have the following values: + +.ls 4 +.ls 12 absent +If the entire subscript is absent, or if N is absent the tape is opened +at the current position, i.e., the tape is not physically moved. +.le +.ls N >= 1 +If an integer number greater than or equal to 1 (one) is given, +the tape is opened positioned to that file. +.le +.ls N == EOT +If the file number is given as "EOT" or as "eot", the tape is opened +positioned at EOT. +.le +.le + +If called with a non "mt"-prefixed file name, MTOPEN assumes that the file +is a regular disk resident binary file, and attempts to open the named +file. A program which normally reads directly from a magtape device may +therefore be used to read from a regular binary file, or from the +standard input (the special file "STDIN"). + +The following access modes are recognized by MTOPEN: +.ls 4 +.ls 12 READ_ONLY +The device is opened for reading, positioned to the beginning of +a file, BOT, EOT, or to the "current position". +Any attempt to write to the device will cause an error. +.le +.ls WRITE_ONLY +The device is opened for writing, positioned to the beginning of a file, +BOT, EOT, or to the "current position". When the file is subsequently +closed, a new EOT mark will be written. Existing tape files may be overwritten. +.le +.ls APPEND +The device is opened for writing, positioned at the end of tape (EOT). +Append mode must not be used with blank tapes. Note that the significance +of APPEND mode is different for MTOPEN than for a regular file open; +the tape is to be extended by adding a new file, whereas in FIO it is +the file itself which is extended. +.le +.ls NEW_TAPE +The device is opened for writing, positioned at BOT. Any existing +data on the tape is overwritten. Recommended mode for blank tapes. +.le +.le + +FIO can read any tape with a maximum record size less than or equal +to the buffer size. In buffered mode, FIO normally writes records equal +in size to the FIO internal buffers. Smaller records may be written if +FLUSH or CLOSE is called; records of any size may be written in unbuffered +mode. + +The third argument to MTOPEN is used to set the FIO buffer size. +If the buffer size is given as zero, a default value is used. +The default value chosen depends on the mode of access and upon the +maximum record size permitted by the host system. MTOPEN will automatically +allocate the maximum size FIO buffer if the tape is opened for reading. +The FIO buffer size may be changed in an FSET call anytime before +the first buffered i/o on the file. + +.sh +3.2 Ordinary I/O to a Magtape Device + + Seeks are not permitted on magtape files. A tape may be opened for +reading or for writing, but not for both at the same time. A magtape +device is much like the standard input and output; STDIN is read only, +STDOUT is write only, and seeking is not permitted on either. STDIN +differs from ordinary files in that data may continue to be read after +an EOF; the same is true of magtape files. If a read returning EOF is +followed by another read on a magtape device, the second read will +access the first record of the next file. Once EOT is reached, every +read will return EOF. There is no way to advance beyond EOT on a read. +An EOF mark may only be written by closing the tape (see next section). + +.sh +3.3 Closing a Magtape + + The CLOSE function is called either explicitly by the user task, +or implicilty by the IRAF main upon normal or abnormal task termination. +If the file was opened for writing, CLOSE flushes the output buffer and +writes an EOT mark at the current position. A file opened for writing +is left positioned ready to write the first record of the next file on +the tape (i.e., after the EOF of the file just written). A file opened +for reading, if closed immediately after reading EOF, is left positioned +ready to read or write the first record of the next file on the tape. + +.sh +3.4 Bytes and Records + + Upon input, the size of a record will be rounded up to an integral +number of "chars". No data will be lost, but one or more extra bytes of +data may be added. A tape conversion program which must deal with +odd-sized records must know the size of tape records to properly extract +the data. FIO cannot write odd-sized records. + +The binary i/o routines do not transform the data in any way, +including byte swapping. If byte swapping is necessary, the conversion +program must do the byte swapping explicitly. The predefined machine +constants BYTE_SWAP and WORD_SWAP indicate whether byte or word swapping +is necessary on the local machine (word swapping refers to the two 16 bit +words in a long integer). Floating point data should not be stored in +binary on tape, unless the tape is to be read only by the machine which +wrote it. + +Routines are available elsewhere in the PI for manipulating bytes. +The routines BYTMOV, BYTSWP, and BYTPAK are particularly useful for cracking +foreign tapes. + +.sh +3.5 Low level I/O to a Magtape + + The asynchronous, unbuffered FIO routines AREAD, AWRITE, and AWAIT +may be used to perform record i/o on a magtape. A single tape record +is read or written by each call; the FIO buffer is not used. AWAIT must +be called after each read or write, before initiating the next i/o transfer. +Although in general it is unwise to mix buffered and unbuffered i/o on +the same file, it is safe to perform one or more unbuffered transfers +immediately after opening a file, before performing any buffered i/o. + +If an application must do something peculiar, like write an odd-sized +record, the MTIO z-routines may be called directly. The z-routines transfer +data in units of machine bytes. + +.sh +3.6 CL level tape commands + + The four routines ALLOCATE, DEALLOCATE, REWIND and MTSTATUS will be +available at the CL level. A tape drive ("mta", "mtb", etc.) must be +explicitly allocated before it can be accessed with MTOPEN. +A drive may be allocated to only one user at a time. MTIO keeps track +of the position of an allocated tape; once a drive has been allocated, +the tape must not be manually positioned by the user. Tapes can be +changed without reallocating the drive provided REWIND is first used to +rewind the tape. DEALLOCATE automatically rewinds the tape before +deallocating the drive. MTSTATUS tells whether or not a particular drive +has been allocated, and if so, gives the name of the owner, the density, +whether not the device is physically open, and so on. + +.sh +3.6 Example + + The following program reads an ASCII card image file from the +input tape and converts it to an ordinary text stream, which is written +to the standard output. This program is insensitive to the number of +cards per tape record, and may be used to read card image disk files +as well as tape files. + +The following CL commands might be entered to read the second card image +file on the 800 bpi input tape into file "myfile", converting to lower case: + +.ks +.nf + cl> allocate mta + cl> rcardimage "mta800[2]" | lcase > myfile +.fi +.ke + + +The source for the program "rcardimage" follows: + + +.ks +.nf +# RCARDIMAGE -- CL callable task which reads a card image +# file, writing the converted cards to the standard output. + +procedure rcardimage() + +char filename[SZ_FNAME] +int input, mtopen() + +begin + call clgstr ("filename", filename, SZ_FNAME) + input = mtopen (filename, READ_ONLY, 0) + call read_card_image_file (input, STDOUT) + call close (input) +end +.fi +.ke + + + +.ks +.nf +define SZ_CARD 80 + +# READ_CARD_IMAGE_FILE -- Read the named card image file, convert +# to a regular character stream, and write to the output file. + +procedure read_card_image_file (input, output) + +int input, output # input, output files +char cardbuf[SZ_CARD] # raw card +char linebuf[SZ_CARD+1] # add 1 char for newline +int last_char +int read() +errchk read, putline + +begin + # Read successive cards until EOF is reached. Unpack the + # card, strip trailing whitespace, and write the processed + # line to the output file. + + while (read (input, cardbuf, SZ_CARD) != EOF) { + call chrupk (cardbuf, 1, linebuf, 1, SZ_CARD) + + # Strip trailing whitespace, add newline. + last_char = 0 + for (ip=1; ip <= SZ_CARD; ip=ip+1) + if (! IS_WHITE (linebuf[ip])) + last_char = ip + + linebuf[last_char+1] = '\n' + linebuf[last_char+2] = EOS + + call putline (output, linebuf) + } +end +.fi +.ke +.endhelp + + +.helpsys mtio Nov83 "MTIO Interface Detailed Design" +.sh +Strategies for Interfacing MTIO + + The specifications for MTIO have been kept as simple as possible for maximum +machine independence. There is only one high level call, MTOPEN, which is +used to open the device; it does little more than call FIO. Thereafter +everything is done through the six FIO z-routines: + +.nf + zopnmt (filespec, access_mode; channel|ERR) + zclsmt (channel) + zardmt (channel, buffer, maxbytes, one_indexed_byte_offset) + zawrmt (channel, buffer, nbytes, one_indexed_byte_offset) + zawtmt (channel; nbytes|ERR) + zsttmt (channel, parameter; long_value) +.fi + +These z-routines may be written in any language so long as they are SPP (i.e., +Fortran) callable. The "filespec" argument is a packed string. The access +modes and ERR code are system constants defined in <iraf.h>. Channel may be +anything you wish. The file offsets will always be zero since the magtape +device is a streaming (sequential) device. + +Be sure that the z-routines, if written partially in the SPP language, do not +make calls to high level program interface routines, especially any which do +i/o. Doing so violates the reentrancy restrictions of the SPP language (and +of Fortran). It may also lead to problems with the way libraries are searched. +More explicitly, any calls to error or iferr, to any of the FIO routines, or to +any of the printf or scan routines are absolutely forbidden. It is ok to use +the string primitives (because they do not do any i/o or call error), but it +is best to avoid even those if possible. It is perfectly all right to call +other z-routines provided they do not directly or indirectly call you back +(none will call mtio). + +.sh +Virtual Device Model + + Though in general the z-routines may have to be completely rewritten for +a new OS, in fact many systems have functionally similar primitive magtape +interfaces. If your system can be described by the model defined below, +the "z?*.x" files in this directory can be used unchanged, and all you need to +provide are the equivalents of the "zz" prefixed files. + + Basically, the model requires that the zz-routine which opens the magtape +be capable of positioning the tape to the first record of any file, given the +file number to which the tape is positioned at open time. The high level MTIO +interface code is charged with keeping track of the position of the tape at +all times, including while the tape is closed. The high level code does not +explicitly move the tape, though it does implicitly move it during open and when +it commands an i/o operation. The high level code assumes nothing about tape +motions; all zz-primitives return status values stating how many records or +files the tape moved in the last operation. Thus, the details of the function +of a zz-routine can vary depending on the system, without requiring +modifications to the z-routines. + +.ls 4 +.ls (1) +The following open/close primitives are required. ZZOPMT opens a tape +drive positioned ready to read or write the first record of the specified +file. +.ls zzopmt (drive,density,acmode, oldrec,oldfile; newfile; oschan|ERR) +.br +.ls 10 drive +Logical drive number (1,2,3,...). +.le +.ls density +Drive density. A positive integer, i.e., 800, 1600 or 6250. A value +of zero implies that an OS default density is to be selected. +.le +.ls acmode +Access mode. Read_only or write_only. Modes new_file and append are +dealt with by the "newfile" file number parameter. +.le +.ls oldrecord +The number of the record to which we are currently positioned in the +oldfile. We have to know this to know whether or not the file has to +be rewound. The first record is number one. +.le +.ls oldfile +The number of the file to which the tape is currently positioned. +.le +.ls newfile +The number of the file to be opened, where 1 specifies that the tape is +to be rewound. If newfile <= 0, open at EOT. On output, contains the +actual file number of the new file; this may differ from the requested +value if EOT is encountered. It is not an error (as far as the zz-routine +is concerned) to attempt to position to a file beyond EOT. +.le +.ls oschan +The channel number by which the file is to be referred in calls to the +other zz-routines, or ERR if the file cannot be opened. ZZOPMT should +set oschan IMMEDIATELY AFTER PHYSICALLY OPENING THE DEVICE, so that the +error recovery code can close the file if an interrupt occurs. +.le +.le + +.ls 4 zzclmt (oschan, access_mode; nfile) +Close tape. Write a new EOT at the current position if writing. +Returns the file number increment (in the event that a file mark or two +has to be written). +.le +.le + +.ls (2) +The following i/o primitives are required. +.ls zzrdmt (oschan, buf, maxbytes) +Initiate a read of up to maxbytes bytes from the next tape +record into buffer buf. It is not an error if fewer than "maxbytes" +bytes are read; in fact maxbytes will normally be larger than the largest +record on the tape. In general it is difficult to tell if the tape record +was larger than maxbytes; the user code should select a large maxbytes and +consider it an error if the full maxbytes are read. If EOF is encountered, +return a zero byte count in the next call to zzwtmt. +.le +.ls zzwrmt (oschan, buf, nbytes) +Initiate a write of "nbytes" bytes to the tape. It is an error +if all data is not written. +.le +.ls zzwtmt (oschan; nbytes|ERR, nrecord, nfile) +Wait for i/o to complete, and return the number of bytes read or +written in the last transfer; return 0 if we read a tape mark. +Keep returning the same thing in redundant calls. If an error +occurs the error status should be cleared when the next i/o +transfer is initiated. Return nrecord=1 for each tape record read, +but not for file marks. Return nfile=1 if a filemark is skipped. +A read of 0 bytes signifies that EOF was seen, but does not necessarily +imply that a tape mark has been skipped. +.le +.le +.le + +.sh +Program Structure + + The drive must be allocated before MTOPEN is called; MTOPEN verifies +that the drive has been allocated and then calls FIO to install the magtape +device and open the desired file on the drive. Allocating a file involves +a call to the OS to allocate and/or mount the device, followed by creation +of the device lock file in the dev$ directory. The lock file is used to +indicate that the drive has been allocated, and to keep track of the tape +position when the drive is closed. + + +.ks +.nf + mtopen + mt_parse_filespec + mt_get_tape_position + various FIO routines + fopnbf + zopnmt + zzopmt + zsttmt + fset [to set buffer size] + + + Structure of the MTOPEN Procedure +.fi +.ke + + +The ZCLSMT procedure is called by CLOSE to close a file opened with MTOPEN. +CLOSE may be called by the user, by the IRAF main if the task completes w/o +closing the file, or during error recovery. An error occurring during MTOPEN +may result in a call to CLOSE. We do not have to worry about reentrancy here +because none of the MTIO z-routines called by ZOPNMT call error (they will +return an ERR status to FOPNBF and therefore terminate before ZCLSMT is called). + + +.ks +.nf + close + zclsmt + mt_update_lockfile + smark,salloc,sfree + fvfn_to_osfn + mktemp + zopntx + zputtx + zclstx + zfdele + zfrnam + fatal + zzclmt + + + Structure of the ZCLSMT procedure +.fi +.ke + + +The i/o procedures keep track of the number of records read or written and +ensure that we do not read past EOF or EOT. We need to keep track of the +number of records written so that we can detect a null file write. + + +.ks +.nf + aread awrite + zardmt zawrmt + zzrdmt zzwrmt + + + await + zawtmt + zzwtmt + + + Structure of the I/O Procedures +.fi +.ke + + +The final routine is the ZSTTMT status procedure. This routine is self +contained, except that access to the MTIO common is required to get the +access mode (which determines the default buffer size). + + +.sh +Semicode + + We do as much of the work in MTOPEN as we can, since it is the only +high level, machine independent routine we have (we can call anything in this +routine without reentrancy problems). We check that the drive has been +allocated (to us), allocate an mtio device descriptor, check that the same +drive has not been reopened, parse the filespec and save the pieces in the +descriptor (for later use by ZOPNMT), read in the current tape position from +the lock file, and then call fopnbf which in turn calls ZOPNMT to open the +drive and position to the desired file. + + +.tp 8 +.nf +int procedure mtopen (filespec, access_mode, bufsize) + +begin + if (file not mt-prefixed) { + call open to open regular binary file + return (file descriptor returned by open) + } + + get mtio device descriptor slot but do not allocate it yet + if (slots all taken) + error: magtape device multiply opened (filespec) + call mt_parse_filespec to break out drivename, density, + filenumber and set up mtio device descriptor + if (drive is already open) + error: magtape device multiply opened (filespec) + check that the named drive has been allocated to us + if (drive not allocated to us) + error: magtape not allocated (filespec) + decode lock file to get old position of drive, save in + device descriptor + + call fopnbf to install the magtape device and open file + if (bufsize is nonzero) + call fset to set non-default FIO buffer size + + return (file descriptor) +end +.fi + + +ZOPNMT is passed a mostly initialized magtape file descriptor in the mtio +common by MTOPEN. The "filespec" string is not actually used by ZOPNMT. +We physically open the file and set up the remaining fields in the tape file +descriptor. NOTE: if an interrupt occurs during the call to ZZOPMT, +ZCLSMT will be called to perform error recovery. If this happens the +position of the tape is undefined. + + +.ks +.nf +procedure zopnmt (filespec, access_mode; chan|ERR) + +[we are passed the mtio file descriptor in the mtio common] + +begin + set flag for ZCLSMT in case we are interrupted + call ZZOPMT to open tape positioned to the beginning of the + indicated file. + + if (cannot open tape) + return (chan = ERR) + if (actual file < requested file) + if (reading) + set at-eof flag and at_eot flag, so that reads return EOF + else { + call zzclmt to close device + return (mtchan = ERR) + } + + save oschan in descriptor + save actual file number in descriptor + initialize the remaining descriptor fields + + return (chan = mtio descriptor index) +end +.fi +.ke + + +Opening a file for writing and then immediately closing it poses problems. +There is no way to write a zero-length file on an unlabeled tape. We cannot +just write a tape mark because that might mean writing a spurious double +tapemark (false EOT) in the middle of the tape. We could just ignore the +request and not write any file, but that is not quite right either (if a +disk file is opened for writing and then immediately closed, a file is +still created). We compromise by writing a single short record containing +the packed ASCII character string "NULLFILE". The actual length of the +physical nullfile record is machine dependent. + +We may or may not need to bump the file counter when the file is closed; +the OS tells us which. If we have just written a file and ZZCLMT must +write a tape mark, for example, we might be left positioned before EOT, +between the tape marks, or (unlikely) after the tape marks. + +If an error (i.e., console interrupt) occurs while the tape is being +positioned by ZZOPMT, we will be called by the error recovery system +with a garbage mtchan argument. We must be able to detect this type +of call and modify the lock file, marking the position of the tape +as UNDEFINED. The next open call will then automatically rewind the +tape, ensuring accurate positioning. In a normal close we write out +a lockfile identifying the file and record to which the tape is positioned. + + +.ks +.nf +procedure zclsmt (mtchan) + +begin + if (error recovery in progress for ZOPNMT) + mark current position as undefined + else { + if (file was opened for writing but nothing was written) + write out "null file" short record + + call zzclmt (oschan, access_mode; nfiles) + file += nfiles + if (nfiles > 0) + record = 1 + } + + call mt_update_lockfile to save tape status, position + deallocate the mtio device descriptor slot +end +.fi +.ke + + +Ignore all read requests once EOF or EOT has been reached. The only operation +possible at that point is to close the device; there is no rewind or backskip +function for a streaming device. We do not "know" what would happen if we +called ZZRDMT after reaching EOF; this is OS dependent, and we do not want +to require any particular behavior (some systems would return the first +record of the next file, others would keep returning EOF). + + +.ks +.nf +procedure zardmt (mtchan, buf, maxbytes, offset) + +begin + if (not at EOF or EOT) + call zzrdmt (oschan, buf, maxbytes) +end +.fi +.ke + + +We cannot hit EOF or EOT on a write, so merely post the request and return. +Ignore the "offset" parameter since magtape is a streaming device. + + +.ks +.nf +procedure zawrmt (mtchan, buf, nbytes, offset) + +begin + call zzwrmt (oschan, buf, nbytes) +end +.fi +.ke + + +FIO insures that ZAWTMT is called once and only once after every asynchronous +i/o read or write request. Once we hit EOF or EOT on a read, we return +EOF (nchars = 0) on every subsequent request until the file is closed. +After each real data transfer we update the record,file counters as +directed by the OS. + + +.ks +.nf +procedure zawtmt (mtchan; status) + +begin + if (reading and at EOF or EOT) + return (status = 0) + + call zzwtmt (oschan, nchars, nrecords_skipped, nfiles_skipped) + if (nfiles_skipped > 0) { + set at-EOF flag + record = 1 + } + + file += nfiles_skipped # keep track of position + record += nrecords_skipped + nrecords += nrecords_skipped # count records rd|wr + + status = nchars +end +.fi +.ke + +.sh +Error Recovery + + Error recovery during magtape operations is tricky since we are trying +to keep track of the position of the drive. If an error occurs while the +tape is being positioned by ZZOPMT we must detect the condition and mark the +position of the tape as indefinite (forcing a rewind on the next open). +If an interrupt occurs while positioning to EOT for a write, we do not +want ZZCLMT to write the new EOT mark somewhere in the middle of the tape, +truncating the tape. + + Interrupts or other errors while reading or writing are comparatively +harmless. Interrupting a write results in a short but otherwise normal +file on the tape; interrupting a read leaves us positioned to some +arbitrary record within the file (harmless). Conceivably a read +could be interrupted just as we were reading a tape mark, causing the +file position to be lost. We have not protected against this. + +.sh +Data Structures + + The mtio device descriptor structure describes the status of each magtape +device in use. The maximum number of magtape files which can be open at one +time is the maximum number of tape drives. Only one file can be open on each +drive at a time. + + +.ks +.nf + struct mtiodes { + int mt_drive # 1,2, etc. (c.t. a,b,...) + int mt_density # 0,800,1600,6250, etc. + int mt_oldfile # file number at open time + int mt_oldrecord # record number at open time + int mt_file # file being accessed (0=EOT) + int mt_record # next record to be accessed + int mt_nrecords # nrecords read/written + int mt_acmode # access mode + int mt_oschan # OS channel number + int mt_ateof # true when at EOF + int mt_ateot # true when at EOT + } mtiocom[MAX_TAPES] +.fi +.ke + + +When a device is allocated, a lock file is created in the system logical device +directory "dev$". For example, the lock file for magtape unit A is the +file "dev$mta.lok". This is a human readable text file; it is typed out on +the terminal when the user types "devstatus mta". The lock file is updated +when the drive is closed and deleted when the drive is deallocated. The drive +is automatically deallocated when the user logs out of the CL. + + +.ks +.nf +Sample Lock File: + + # Magtape unit 'mta' allocated to 'user' Sun 20:06:13 27-Nov-83 + current file = 4 + current record = 1 + 72 records read, EOF seen +.fi +.ke + + +The preliminary comments are written when the lock file is created, i.e., +when the device is allocated. Any number of comment lines are permitted. +They are merely copied when ZCLSMT updates the lock file. The remaining +records are written by ZCLSMT to record the current position of the tape, +and to inform the user of the device status. diff --git a/sys/mtio/doc/newdriver.notes b/sys/mtio/doc/newdriver.notes new file mode 100644 index 00000000..3ef2ca7f --- /dev/null +++ b/sys/mtio/doc/newdriver.notes @@ -0,0 +1,517 @@ + NEW IRAF MAGTAPE DRIVER + October 1991 + (design notes) + + +1. User Interface + +1.1 Magtape Specification + + Old format: + + mtX.density[file.record] + + New format: + + mtX['['file[.record][:(param|param@|param=value):...]']'] + + e.g., + + mtexb1[4:nb:se@:ts=1200:so=/dev/ttya8] + + mtX is used only as an index into the devices file, and is not + parsed. The tape density is no longer singled out as a special + parameter. If any parameters are specified, these parameters + override those given in the tapecap file entry. + + +1.2 TAPECAP file + + Current Format: + + irafname [%NNNN] device aliases + e.g., mtb.9 nrst8 rst8 nrst0 rst0 + + Revised Format: + + name[|alias|...]:cap=value:...:[tc=name:] + + Device parameters are specified in termcap file format. + + mta|mtexb1|Exabyte drive 1:\ + :dv=nrst0:rd=rst0:\ + :al=rst0,rst8,rst16,rst24,nrst0,nrst8,nrst16,nrst24:\ + :tc=exb-sunst: + exb-sunst|Exabyte via SunOS ST driver:\ + :bs#0:dt=Exabyte:fs#2200:mr#65535: + + This feature will use the new GTY interface. Modifications are + required to 1) allow specification of parameters at open time which + will override those given in the file, and 2) make the use of a # + for numeric parameters optional (accept either # or =). + + +1.3 I/O Monitoring + + Device and tape type and capacity are given in tapecap. + Keep track of tape usage along with file position in lock file. + Lock file provides simple means to track usage. A real time + display may optionally be provided by the driver writing to + a tape monitoring window via a file or socket, with the name + given in tapecap or on the command line. + + +2. Code Changes + + OS,KI + zfiomt.c - all host versions are affected as calling sequence + has changed. Network interface is affected (new tape system + and old iraf kernel servers are incompatible). + + The UNIX version of the new driver needs additional code to + optionally log status information to a file or special file. + Support for tcp/ip status logging as well? + + ETC + Device allocation code is affected by the devices file syntax + changes. + + MTIO + Must be changed to reflect magtape specfication and devices file + specification changes, i.e., the density parameter is omitted, + the device name is not quite the same thing, and a general + device parameter mechanism is added. + + The feature to keep track of the amount of tape used is device + and host independent (given adequate parameterization in the + devices file) hence can be implemented in MTIO. Output to the + user, and preservation of tape usage status over device closes, + will be via the .lok file and DEVSTATUS. + + The MTIO driver Z routines seem largely unaffected, except for + the status routine and the i/o routines, which need support added + for blocked devices. + + +3. Device Driver + +3.1. Device Classes + + generic generic device (open/close/read/write) + reel 800,1600,6250 bpi 1/2inch reel tape + cartridge various QIC formats - fixed size blocks + exabyte variable size blocks + dat variable size blocks + + +3.2. Device Parameters + +[[NOT KEPT UP TO DATE - refer to os$zfiomt.c for latest version. ]]]] + + CODE TYPE DEFAULT DESCRIPTION + + bs i 0 device block size (0 if variable) + dn i none density (bpi) + dt s generic drive type + fs i 0 filemark size (Kb) + mr i 65535 maximum record size + or i 63360 optimum record size + rs i 0 record gap size (bytes) + ts i 0 tape capacity (Mb) + tt s unknown tape type + + al s none device allocation info + dv s required no-rewind device file + rd s none rewind device file + so s none status output device file or socket + + bo b no BSF positions to BOF + fc b no device does a FSF on CLRO + ir b no treat all read errors as EOF + nb b no device cannot backspace + nf b no rewind and space forward to backspace file + np b no disable all positioning ioctls + ow b no backspace and overwrite EOT at append + re b no read at EOT returns ERR + rf b no use BSR,FSR to space over filemarks + se b no device will position past EOT in a read + sk b no skip record forward after a read error + wc b no OPWR-CLWR at EOF writes null file + + bf i builtin BSF ioctl code + br i builtin BSR ioctl code + ct i builtin MTIOCTOP code + ff i builtin FSF ioctl code + fr i builtin FSR ioctl code + ri i builtin REW ioctl code + + +3.3. Host Level Device Operations + + open (FILE I/O OPERATIONS) + close + read + write + wait (not used) + + FSR (POSITIONING IOCTLS) + BSR + FSF + BSF + REW + + WEOF (not used) + EOM (not used) (SunOS - space to end of media) + + +3.4. Device Characteristics + + open No known system/device dependencies. + + close For a tape opened read-only, close positions to after + the filemark of the current file on some SysV systems. + This makes it impossible to rewind a device opened + no-rewind (after the open/rewind/close the tape is + left at the beginning of the second file). On such + systems the tape can only be rewound (left rewound + at close) by opening and closing the rewind device. + + It is assumed that when a tape opened for writing is + closed an EOT mark is written, and the tape is left + positioned after the filemark of the last file + written, ready to write the next file (true on all + known systems, with some variations in how the EOT + is represented). + + read A read where the record size exceeds the size of the + read request is assumed to be an error. + + A read at end of file should return a zero byte + count and leave the file positioned after the file + mark. Some devices may return ERR when a tape mark + is read. + + If the device block size is zero it is assumed that + records can be any size up to the max record size, + and that successive records can vary in size. + + If the device block size is nonzero it is assumed + that the byte count for read and write requests (the + record size) should be a multiple of the device + block size. Multiple physical device blocks are + read or written to satisfy an i/o request. On a + read, all notion of the record size is lost, i.e., + a read of N blocks will return N blocks regardless + of the blocking factor used in a write. + + A read at EOT may leave the tape positioned after + the file mark just read or may result in a zero byte + count being returned with no affect on the tape + position. + + Following a read error when reading a data record the + tape may be left 1) before the bad record, 2) after + the bad record, 3) in an undefined position, e.g., + partway through the record. + + write For variable record devices each write is assumed + to write a tape record the size of the output buffer. + For fixed block devices it is assumed that the size + of the write request must be an integral multiple of + the block size, and that multiple physical device + blocks will be written to satisfy the request. + + wait Wait (asynchronous i/o) is not currently used and + is emulated in the driver. Some systems (Ultrix + and SunOS 4.1) provide facilities for multi-buffered + asynchronous i/o which the iraf driver may make use + of in the future. + + FSR Forward skip record. Some systems permit a FSR over + a filemark and some do not. + + BSR Backward skip record. Some systems permit a BSR over + a filemark and some do not. + + FSF Forward skip file. The tape is assumed to be left + positioned after the filemark and before the first + record of the next file. + + BSF Backward skip file. Some systems leave the tape + positioned on the BOT side of the filemark, others + leave the tape positioned to just before the first + record of the file following the filemark. A BSF with + a zero count may or may not rewind the current file. + + REW Rewinds the tape, leaving the tape positioned to BOT. + No known system/device dependencies. See the note + on the "close" operation, above. + + WEOF Not currently used. + + +3.5. Driver Options + + overwrite filemark (reel tapes only) + disable all backspace operations + skip record forward after a read error + i/o logging to specified output device + + +3.6. Driver Functions + + ZZOPMT - open and position to desired file + zmtopen + zmtclose + zmtposition + zmtrew + zmtfsf + zmtbsf + zmtfsr + zmtbsr + + ZZCLMT - close + zmtclose + + ZZRDMT - read next record + read + zmtbsf + + ZZWRMT - write record + write + + ZZWTMT - wait for i/o and return byte count + + ZZRWMT - rewind tape + zmtopen + zmtclose + + ZZSTMT - return device parameters + + +3.7. Notes on Specific Devices + +3.7.1 Device Characteristics + +3.7.2 Exabyte Drivers + + SunOS 4.1 ST Driver + BSF positions to BOT side of filemark (conventional behavior). + Driver does not allow positioning to after EOT. + Bug where driver loses track of file position is fixed. + + SunOS 4.0.3 ST Driver + BSF positions to first record of file following file mark. + BSF 0 rewinds the current file. + Driver does not allow positioning to after EOT, i.e., when + positioning to EOT, after two successive zero reads, + it is NOT necessary to backspace over the filemark. + Driver tries to keep track of current file position but has + a bug which causes it to zero its counter when appending + a file to a tape within open/close. + This bug has two ramifications: + o Will not position to a file before where it thinks + BOT is. + o Will rewind to get to file it thinks is at BOT. + + Earlier (SunOS 3.X) Sun drivers defaulted to fixed block mode, and + probably had other significant differences from the current + round of drivers. + + Ciprico RT Driver + Both fixed and variable block device entries. + rfsd_pr_errlog can get set to 1|2|8 in driver (/dev/mem) to + turn on status messages. Variable is commonly set to 2 + to get tape position readouts, but this will interfere + with iraf networking. + BSF positions to BOT side of filemark (conventional behavior). + EOT is indicated by two file marks, and a read of the second mark + will leave the tape positioned to after the mark, requiring + a backspace to position to EOT (conventional behavior). + Status (NOP) ioctl implemented, but file/record count always zero. + + Sparcstation (4.1) ST driver + BSR over a filemark confuses the driver - looks like it thinks + it saw the EOF when reading in the forward direction. + The next read returns EOF and then the real EOF is read, + so 2 EOFs in a row are seen (looks like EOT). The file + count in the driver gets messed up. + BSF causes all subsequent reads to return immediately with ERR. + One (sometimes two!) REW ioctls are required to clear. + FSF and FSR work. + + R-Squared driver for Exabyte (Sun-3 running 4.1) + Opens drive in fixed block mode. + Read at EOF returns IOERR (driver bug). + Status (NOP) ioctl not implemented. + + +3.8. Pseudocode for Driver Functions + + +# ZZOPMT -- Open the magtape device and position to the given file. + +procedure zzopmt (device, acmode, oldrec, oldfile, newfile, chan) +begin + # Open device for positioning. + open no-rewind raw device read-only + + # Do not move the tape if opened newfile=0. + if (newfile != 0) { + # Rewind to get to known position if position uncertain. + if (current position unknown) { + rewind tape + oldrec = 1 + oldfile = 1 + } + + # Position to given file. + newfile = zmtfpos (chan, oldfile, oldrec, newfile) + } + + # Reopen if necessary for i/o. + if (need write access) + reopen device for writing +end + + +# ZZCLMT -- Close the magtape device. + +procedure zzclmt (chan, acmode, nrecords, nfiles, status) +begin + close device + + nfiles = 0 + nrecords = 0 + + if (acmode == read && device: FSF on close read-only) + nfiles = 1 + else if (acmode == write && !(at BOF && device: no EOF if no write)) + nfiles = 1 +end + + +# ZZRDMT -- Initiate a read of the next tape record into the user buffer. + +procedure zzrdmt (chan, buf, maxbytes) +begin + physically read tape + save read status for zzwtmt +end + + +# ZZWRMT -- Initiate a write of the next tape record. + +procedure zzwrmt (chan, buf, nbytes) +begin + physically write record + save write status for zzwtmt +end + + +# ZZWTMT -- Wait for i/o to complete and return the byte count and the +# change to the tape position in files and records. + +procedure zzwtmt (chan, nrecords, nfiles, nbytes) +begin + nrecords = 0 + nfiles = 0 + + if (io error) { + nbytes = ERR + } else if (read 0 bytes) { + if (at BOF) { + # At EOT. + if (device: read at EOT will go past EOT) + if (device: cannot backspace) { + zmtrew() + nfiles = -ARB + nrecords = -ARB + } else + zmtbsf (1) + } else + nfiles = 1 + } else { + nrecords = 1 + clear at BOF flag + } +end + + +# ZZRWMT -- Rewind the named device. + +procedure zzrwmt (device, status) +begin + if (device: rewind device specified) + close (open (rewind-at-close device read-only)) + else { + open no rewind device + zmtrew + close + } +end + + +# ZMTFPOS -- Position to the indicated file. + +int procedure zmtfpos (chan, oldfile, oldrec, newfile) +begin + # Already positioned to desired file; don't do anything. + if (newfile == oldfile && oldrec == 1) + return (newfile) + + # Move the tape. + if (newfile == 1) { + # Rewind. + zzrwmt() + } else if (newfile <= oldfile && newfile != EOT) { + # Backspace to desired file. + if (device: cannot backspace) { + zmtrew() + oldfile = 1 + oldrec = 1 + goto fwd_ + } else if (device: BSF positions to BOF) { + zmtbsf (oldfile - newfile) + } else { + zmtbsf (oldfile - newfile + 1) + zmtfsf (1) + } + } else { + # Space forward to desired file or EOT. +fwd_ while (oldfile < newfile || newfile == EOT) { + n = read (next record) + if (n == 0 && oldrec == 1) { + # At EOT. + if (device: read at EOT will go past EOT) { + if (device: cannot backspace) { + newfile = oldfile + zmtrew() + oldfile = 1 + oldrec = 1 + goto fwd_ + } else + zmtbsf (1) + } + if (writing && device: overwrite EOF) { + if (!device: cannot backspace) { + if (device: BSF positions to BOF) + zmtbsf (0) + else { + zmtbsf (1) + zmtfsf (1) + } + } + } + break + } else if (n > 0) + zmtfsf (1) + + oldfile++ + oldrec = 1 + } + newfile = oldfile + } + + return (newfile) +end diff --git a/sys/mtio/mkpkg b/sys/mtio/mkpkg new file mode 100644 index 00000000..2c0ce77f --- /dev/null +++ b/sys/mtio/mkpkg @@ -0,0 +1,48 @@ +# Make the MTIO routines. + +$checkout libsys.a lib$ +$update libsys.a +$checkin libsys.a lib$ +$exit + +zzdebug: + $checkout libsys.a lib$ + $update libsys.a + $checkin libsys.a lib$ + + #$set XFLAGS = "$(XFLAGS) -qx" + $omake zzdebug.x + $link -z zzdebug.o + ; + +libsys.a: + #$set XFLAGS = "$(XFLAGS) -qx" + + mtalloc.x mtio.h <time.h> + mtcache.x mtcache.com mtio.com mtio.h <config.h> <error.h> + mtcap.x mtio.h + mtclean.x mtio.h <finfo.h> <xalloc.h> + mtdealloc.x <error.h> + mtdevall.x <knet.h> <xalloc.h> + mtencode.x + mtfile.x + mtfname.x mtio.h + mtglock.x mtio.h + mtgtyopen.x mtio.h + mtlocknam.x mtio.h <chars.h> <ctype.h> + mtneedf.x mtio.h + mtopen.x mtio.com mtio.h <config.h> <fset.h> <knet.h> <mach.h> + mtparse.x mtio.h <config.h> <ctype.h> + mtpos.x mtio.h + mtrdlock.x mtio.com mtio.h <config.h> + mtrewind.x mtio.h <error.h> + mtskip.x <fset.h> + mtstatus.x + mtupdlock.x mtio.com mtio.h <config.h> <knet.h> <mach.h> + zardmt.x mtio.com mtio.h <config.h> <knet.h> + zawrmt.x mtio.com mtio.h <config.h> <knet.h> + zawtmt.x mtio.com mtio.h <config.h> <knet.h> + zclsmt.x mtio.com mtio.h <config.h> <knet.h> + zopnmt.x mtio.com mtio.h <config.h> <knet.h> + zsttmt.x mtio.com mtio.h <config.h> <fio.h> <knet.h> <mach.h> + ; diff --git a/sys/mtio/mtalloc.x b/sys/mtio/mtalloc.x new file mode 100644 index 00000000..2e21be1c --- /dev/null +++ b/sys/mtio/mtalloc.x @@ -0,0 +1,64 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <time.h> +include "mtio.h" + +# MTALLOCATE -- "Allocate" a tape drive by writing the lock file. We do +# not actually call the OS to physically allocate the drive; that is done +# by our caller (e.g., xallocate, in etc$xalloc.x). The lock file is no longer +# used to gain exclusive access to the device; it is now used only to keep +# track of the tape position after process termination. + +procedure mtallocate (mtname) + +char mtname[ARB] #I device name + +int fd, junk +pointer sp, lockfile, mtowner, userid, timestr, device +errchk open, mtparse +long clktime() +int open() + +begin + call smark (sp) + call salloc (device, SZ_FNAME, TY_CHAR) + call salloc (lockfile, SZ_FNAME, TY_CHAR) + call salloc (mtowner, SZ_FNAME, TY_CHAR) + call salloc (userid, SZ_FNAME, TY_CHAR) + call salloc (timestr, SZ_TIME, TY_CHAR) + + # Get name of lockfile used by the given device. + call mtparse (mtname, Memc[device], SZ_FNAME, junk, junk, junk, 0) + call mt_glock (mtname, Memc[lockfile], SZ_FNAME) + + # Open lock file and write out unit, owner, time allocated, etc. + # Overwrite any existing lockfile. We are called only after + # physically allocating the device at the host system level + # (and are not called if the device is already allocated), so this + # is safe. + + iferr (call delete (Memc[lockfile])) + ; + fd = open (Memc[lockfile], NEW_FILE, TEXT_FILE) + + call cnvtime (clktime(long(0)), Memc[timestr], SZ_TIME) + call getuid (Memc[userid], SZ_FNAME) + + call fprintf (fd, "# Magtape unit %s status %s user %s\n") + call pargstr (Memc[device]) + call pargstr (Memc[timestr]) + call pargstr (Memc[userid]) + + # Assume initially that the tape position is undefined. This will + # cause the tape to be rewound on the first open, and thereafter + # the position will be defined unless an i/o error occurs. + + call fprintf (fd, "file = -1\n") + call fprintf (fd, "record = -1\n") + call fprintf (fd, "nfiles = 0\n") + call fprintf (fd, "tapeused = 0 Kb\n") + call fprintf (fd, "pflags = 0\n") + + call close (fd) + call sfree (sp) +end diff --git a/sys/mtio/mtcache.com b/sys/mtio/mtcache.com new file mode 100644 index 00000000..b3533f3f --- /dev/null +++ b/sys/mtio/mtcache.com @@ -0,0 +1,9 @@ +# MTIO savepos cache. + +int c_modified[SZ_CACHE] +int c_mtdes[LEN_DEVPOS,SZ_CACHE] +char c_device[SZ_DEVICE,SZ_CACHE] +char c_iodev[SZ_IODEV,SZ_CACHE] +char c_lkname[SZ_LKNAME,SZ_CACHE] + +common /mtcacm/ c_modified, c_mtdes, c_device, c_iodev, c_lkname diff --git a/sys/mtio/mtcache.x b/sys/mtio/mtcache.x new file mode 100644 index 00000000..87b68bdb --- /dev/null +++ b/sys/mtio/mtcache.x @@ -0,0 +1,199 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <config.h> +include <error.h> +include "mtio.h" + +.help mtcache +.nf _________________________________________________________________________ +MTCACHE -- Cache the magtape position (file and record) between file opens. +The lock file is used for permanent storage, but is only read and written +when necessary. When multiple tape files are accessed by a single task the +tape position is kept in the cache between successive MTOPEN - CLOSE file +accesses. + + mt_getpos (mtname, mt) + mt_savepos (mt) + mt_sync (status) + mt_clrcache () + +SAVEPOS updates the tape descriptor in the cache; SYNC updates the cache +on disk in the lock file. SYNC is automatically called at task termination +time (including during abnormal termination), but is not normally called +when a tape file is closed. + +ERROR RECOVERY is a bit tricky. If error recovery takes place the ONERROR +procedures are called first thing, before FIO cleanup takes place. That means +that SYNC will be called while the magtape channel is still open, and before +SAVEPOS has been called by ZCLSMT (which is called by fio_cleanup which is +called by the main). If an error occurs on a magtape channel we want to +record an undefined file position. We must see that SAVEPOS is called at +file open time to mark the cache for updating, causing SYNC to write an +undefined position lock file when the error occurs. ZCLSMT, when called +during error recovery, should set the position to undefined but need not +sync the cache. + +The cache is invalidated at process startup time and at SYNC time in case +the user loads a new tape or runs a different magtape process while the +current process is idling between tasks. In other words, the cache is only +valid while the task using it is executing. +.endhelp ___________________________________________________________________ + +define SZ_CACHE MT_MAXTAPES + + +# MT_GETPOS -- Return the device position descriptor of a drive given its +# unit number. + +procedure mt_getpos (mtname, mt) + +char mtname[ARB] #I device name +int mt #I MTIO descriptor + +int slot +bool streq() +include "mtcache.com" +include "mtio.com" + +begin + # First look in the cache. + for (slot=1; slot <= SZ_CACHE; slot=slot+1) + if (streq (MT_LKNAME(mt), c_lkname[1,slot])) { + call amovi (c_mtdes[1,slot], MT_DEVPOS(mt), LEN_DEVPOS) + call strcpy (c_device[1,slot], MT_DEVICE(mt), SZ_DEVICE) + call strcpy (c_lkname[1,slot], MT_LKNAME(mt), SZ_LKNAME) + call strcpy (c_iodev[1,slot], MT_IODEV(mt), SZ_IODEV) + return + } + + # Get the current position from the lock file, if there is one. + call mt_read_lockfile (mtname, mt) +end + + +# MT_SAVEPOS -- Save the current position in the cache. The entire descriptor +# is saved since most of the information therein is needed for SYNC, even +# though only a portion of the information will be used by GETPOS. + +procedure mt_savepos (mt) + +int mt #I MTIO descriptor + +int prev, slot +bool streq(), strne() +extern mt_sync() +include "mtcache.com" +include "mtio.com" +data prev /0/ +define cache_ 91 + +begin + # Post termination handler to sync the cache at task termination time. + call onerror (mt_sync) + + # Do not update the cache if the file position is undefined. + if (MT_FILNO(mt) <= 0) + return + + # Are we updating an entry already in the cache? + for (slot=1; slot <= SZ_CACHE; slot=slot+1) + if (streq (MT_LKNAME(mt), c_lkname[1,slot])) + goto cache_ + + # Add the entry to the cache. Resync the contents of the old slot + # if it is for a different drive and has been modified since it was + # last synced. + + slot = prev + 1 + if (slot > SZ_CACHE) + slot = 1 + prev = slot + + if (c_modified[slot] == YES && strne(MT_LKNAME(mt),c_lkname[1,slot])) { + call amovi (c_mtdes[1,slot], MT_DEVPOS(0), LEN_DEVPOS) + call strcpy (c_device[1,slot], MT_DEVICE(0), SZ_DEVICE) + call strcpy (c_lkname[1,slot], MT_LKNAME(0), SZ_LKNAME) + call strcpy (c_iodev[1,slot], MT_IODEV(0), SZ_IODEV) + call mt_update_lockfile (0) + } + +cache_ + call amovi (MT_DEVPOS(mt), c_mtdes[1,slot], LEN_DEVPOS) + call strcpy (MT_DEVICE(mt), c_device[1,slot], SZ_DEVICE) + call strcpy (MT_LKNAME(mt), c_lkname[1,slot], SZ_LKNAME) + call strcpy (MT_IODEV(mt), c_iodev[1,slot], SZ_IODEV) + c_modified[slot] = YES +end + + +# MT_SYNC -- Update all modified entries in the cache. Set the position to +# undefined (file=-1) if we are called during error recovery. We are called +# at task termination by the IRAF Main. + +procedure mt_sync (status) + +int status #I task termination status + +int slot +include "mtcache.com" +include "mtio.com" + +begin + # Update the .lok files of any active devices. + for (slot=1; slot <= SZ_CACHE; slot=slot+1) { + if (c_modified[slot] == YES) { + # If called during error recovery mark the file position undef. + if (status != OK) { + call amovi (c_mtdes[1,slot], MT_DEVPOS(0), LEN_DEVPOS) + MT_FILNO(0) = -1 + MT_RECNO(0) = -1 + call amovi (MT_DEVPOS(0), c_mtdes[1,slot], LEN_DEVPOS) + } + + # Update the lockfile. + call amovi (c_mtdes[1,slot], MT_DEVPOS(0), LEN_DEVPOS) + call strcpy (c_device[1,slot], MT_DEVICE(0), SZ_DEVICE) + call strcpy (c_lkname[1,slot], MT_LKNAME(0), SZ_LKNAME) + call strcpy (c_iodev[1,slot], MT_IODEV(0), SZ_IODEV) + + # Ignore errors if we are called during error recovery. + iferr (call mt_update_lockfile (0)) + if (status == OK) + call erract (EA_ERROR) + + c_modified[slot] = NO + c_device[1,slot] = EOS + } + } + + # Invalidate the cache when a task terminates. + call mt_clrcache() + + # If we are called during error recovery, set the file position for + # all open tapes to undefined to prevent mt_savepos from being called + # by zclsmt when the tape file is closed during error recovery. + + if (status != OK) + do slot = 1, MT_MAXTAPES { + MT_FILNO(slot) = -1 + MT_RECNO(slot) = -1 + } +end + + +# MT_CLRCACHE -- Initialize the cache. + +procedure mt_clrcache() + +int slot +include "mtcache.com" + +begin + for (slot=1; slot <= SZ_CACHE; slot=slot+1) { + c_modified[slot] = NO + c_device[1,slot] = EOS + c_lkname[1,slot] = EOS + c_iodev[1,slot] = EOS + call aclri (c_mtdes[1,slot], LEN_DEVPOS) + } +end diff --git a/sys/mtio/mtcap.x b/sys/mtio/mtcap.x new file mode 100644 index 00000000..d3f859fa --- /dev/null +++ b/sys/mtio/mtcap.x @@ -0,0 +1,36 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "mtio.h" + +# MTCAP -- Return the tapecap descriptor for the given magtape device. The +# device is specified by the full specification (as passed to mtopen), hence +# there may be tapecap attributes in the device specification which override +# those in the tapecap file. + +pointer procedure mtcap (mtname) + +char mtname[ARB] #I magtape device specification + +int fileno, recno +pointer sp, device, devcap, cache_gty, gty +pointer mt_gtyopen(), gtycaps(), gtyopen() +errchk mtparse, mt_gtyopen + +begin + call smark (sp) + call salloc (device, SZ_DEVICE, TY_CHAR) + call salloc (devcap, SZ_DEVCAP, TY_CHAR) + + call mtparse (mtname, Memc[device], SZ_DEVICE, fileno, recno, + Memc[devcap], SZ_DEVCAP) + + # Do not return the cached MTIO device entry, as we do not want the + # application to close this with gtyclose. Open a new GTY descriptor + # using the capabilities in the cached entry. + + cache_gty = mt_gtyopen (Memc[device], Memc[devcap]) + gty = gtyopen ("", "", Memc[gtycaps(cache_gty)]) + + call sfree (sp) + return (gty) +end diff --git a/sys/mtio/mtclean.x b/sys/mtio/mtclean.x new file mode 100644 index 00000000..5de01a7e --- /dev/null +++ b/sys/mtio/mtclean.x @@ -0,0 +1,110 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <finfo.h> +include <xalloc.h> +include "mtio.h" + +# MTCLEAN -- Clean the lock file area. This routine is called periodically +# by the system (e.g. during CL startup) to scan the lock file area and +# deleted old lock files. This prevents old lock files which are no longer +# valid from erroneously being used to indicate the current tape position. +# +# What mtclean does is similar to "delete tmp$mt*.lok" except that, since all +# users store lok files in the same area, we don't want to delete all the +# files indiscriminately. The default action is to delete only files for +# unallocated devices, or for devices allocated to the current user for which +# the lok file is more than STALE seconds old. It is harmless to delete a +# lok file unnecessarily in that MTIO will recover automatically, but doing +# so will force the tape to be rewound to regain a known position. + +procedure mtclean (level, stale, out) + +int level #I 0 for default; 1 to delete all .lok files +int stale #I delete lok file if older than stale seconds +int out #I if nonzero, print messages to this file + +int fd, status +pointer list, ip, cp, device +pointer sp, fname, owner, template, lbuf +long fi[LEN_FINFO] + +pointer fntopn() +long clktime() +int gstrmatch(), xdevowner() +int open(), getline(), finfo(), fntgfn() +define del_ 91 + +begin + call smark (sp) + call salloc (lbuf, SZ_LINE, TY_CHAR) + call salloc (fname, SZ_FNAME, TY_CHAR) + call salloc (owner, SZ_FNAME, TY_CHAR) + call salloc (template, SZ_FNAME, TY_CHAR) + + # Get file name template matching all lock files. + call sprintf (Memc[template], SZ_FNAME, "%s%s*%s") + call pargstr (LOCKLDIR) + call pargstr (LOCKFILE) + call pargstr (LOCKEXTN) + + # Open a file list. + list = fntopn (Memc[template]) + + # Examine each file in turn and delete if delete criteria satisfied. + while (fntgfn (list, Memc[fname], SZ_FNAME) != EOF) { + + # If level is nonzero (force-delete) delete unconditionally. + if (level != 0) + goto del_ + + # Open lok file and get device name. + iferr (fd = open (Memc[fname], READ_ONLY, TEXT_FILE)) + next + if (getline (fd, Memc[lbuf]) == EOF) { + call close (fd) + goto del_ + } + if (gstrmatch (Memc[lbuf], "unit ", status, ip) <= 0) { + call close (fd) + goto del_ + } + device = lbuf + ip + for (cp=device; Memc[cp] != EOS && Memc[cp] != ' '; cp=cp+1) + ; + Memc[cp] = EOS + call close (fd) + + # Determine if the device is currently allocated. If the device + # is not allocated delete the lok file unconditionally. If the + # device is allocated to someone else leave the lok file alone. + # If the lok file is allocated to the current user delete it if + # the file is older than the stale value. + + status = xdevowner (Memc[device], Memc[owner], SZ_FNAME) + switch (status) { + case DV_DEVFREE: + goto del_ + + case DV_DEVINUSE: + # Do nothing. + + case DV_DEVALLOC: + # Delete the file if older than the stale value. + if (finfo (Memc[fname], fi) == ERR) + goto del_ + + if (clktime(FI_MTIME(fi)) > stale) { + # Delete the file. +del_ if (out != NULL) { + call fprintf (out, "delete lok file %s\n") + call pargstr (Memc[fname]) + } + iferr (call delete (Memc[fname])) + ; + } + } + } + + call fntcls (list) + call sfree (sp) +end diff --git a/sys/mtio/mtdealloc.x b/sys/mtio/mtdealloc.x new file mode 100644 index 00000000..2f98a972 --- /dev/null +++ b/sys/mtio/mtdealloc.x @@ -0,0 +1,35 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <error.h> + +# MTDEALLOCATE -- Deallocate a previously allocated tape drive. To deallocate +# a drive we try to rewind (any errors, such as drive offline, will result in a +# warning message), and then delete the lockfile. We do not call up the OS to +# deallocate the drive; that is done at a higher level, usually XDEALLOCATE +# (in etc$xalloc.x). + +procedure mtdeallocate (mtname, rewind_tape) + +char mtname[ARB] #I magtape specification +int rewind_tape #I rewind before deallocating drive + +pointer sp, lockfile +errchk mt_glock, syserrs + +begin + call smark (sp) + call salloc (lockfile, SZ_FNAME, TY_CHAR) + + if (rewind_tape == YES) + iferr (call mtrewind (mtname, NO)) + call erract (EA_WARN) + + call mt_sync (OK) + + call mt_glock (mtname, Memc[lockfile], SZ_FNAME) + iferr (call delete (Memc[lockfile])) + ; + + call mt_clrcache() + call sfree (sp) +end diff --git a/sys/mtio/mtdevall.x b/sys/mtio/mtdevall.x new file mode 100644 index 00000000..3f5620d8 --- /dev/null +++ b/sys/mtio/mtdevall.x @@ -0,0 +1,30 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <xalloc.h> +include <knet.h> + +# MT_DEVALLOCATED -- Verify that the named host magtape device is allocated. +# The IRAF device name must already have been translated to a host device +# name before we are called. + +int procedure mt_devallocated (iodev) + +char iodev[ARB] #I host name of device +pointer sp, pk_iodev, pk_owner +int status + +begin + call smark (sp) + call salloc (pk_iodev, SZ_FNAME, TY_CHAR) + call salloc (pk_owner, SZ_FNAME, TY_CHAR) + + # The following assumes that the node! prefix is in iodev. + call strpak (iodev, Memc[pk_iodev], SZ_FNAME) + call zdvown (Memc[pk_iodev], Memc[pk_owner], SZ_FNAME, status) + + call sfree (sp) + if (status == DV_DEVALLOC) + return (YES) + else + return (NO) +end diff --git a/sys/mtio/mtencode.x b/sys/mtio/mtencode.x new file mode 100644 index 00000000..b6bf6b0f --- /dev/null +++ b/sys/mtio/mtencode.x @@ -0,0 +1,44 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +# MTENCODE -- Construct a full magtape device specification. This routine is +# the opposite of MTPARSE. If the file and record numbers are to be omitted +# from the output mtname they should be passed as ERR. + +procedure mtencode (outstr, maxch, device, fileno, recno, attrl) + +char outstr[ARB] #O magtape device specification +int maxch #I max chars out +char device[ARB] #I device name (incl node) +int fileno, recno #I file and record numbers, or ERR +char attrl[ARB] #I tapecap attributes + +int op +int gstrcpy() +int itoc() + +begin + if (fileno != ERR || recno != ERR || attrl[1] != EOS) { + op = gstrcpy (device, outstr, maxch) + 1 + outstr[op] = '['; op = op + 1 + if (fileno != ERR) { + if (fileno == EOT) + op = op + gstrcpy ("EOT", outstr[op], maxch-op+1) + else + op = op + itoc (fileno, outstr[op], maxch-op+1) + } + if (recno != ERR) { + outstr[op] = '.'; op = op + 1 + op = op + itoc (recno, outstr[op], maxch-op+1) + } + if (attrl[1] != EOS) { + if (attrl[1] != ':') { + outstr[op] = ':' + op = op + 1 + } + op = op + gstrcpy (attrl, outstr[op], maxch-op+1) + } + outstr[op] = ']'; op = op + 1 + outstr[op] = EOS + } else + call strcpy (device, outstr, maxch) +end diff --git a/sys/mtio/mtfile.x b/sys/mtio/mtfile.x new file mode 100644 index 00000000..c07a550b --- /dev/null +++ b/sys/mtio/mtfile.x @@ -0,0 +1,24 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +define SZ_NODENAME 9 + +# MTFILE -- Test a filename to see if it is the name of a magtape file. +# A magtape file is characterized by the filename prefix "mt" (ingnoring +# the nodename prefix if any). + +int procedure mtfile (fname) + +char fname[ARB] #I filename to be tested + +int ip, junk +char nodename[SZ_NODENAME] +int ki_extnode() + +begin + ip = ki_extnode (fname, nodename, SZ_NODENAME, junk) + 1 + + if (fname[ip] == 'm' && fname[ip+1] == 't') + return (YES) + else + return (NO) +end diff --git a/sys/mtio/mtfname.x b/sys/mtio/mtfname.x new file mode 100644 index 00000000..7bb92408 --- /dev/null +++ b/sys/mtio/mtfname.x @@ -0,0 +1,29 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "mtio.h" + +# MTFNAME -- Edit the input mtname (magtape file specification) to reference +# the numbered file. The edited mtname is returned in the output string. + +procedure mtfname (mtname, fileno, outstr, maxch) + +char mtname[ARB] #I magtape device specification +int fileno #I desired file number +char outstr[ARB] #O output mtname string +int maxch #I maxch chars out + +int ofileno, orecno +pointer sp, device, devcap + +begin + call smark (sp) + call salloc (device, SZ_DEVICE, TY_CHAR) + call salloc (devcap, SZ_DEVCAP, TY_CHAR) + + call mtparse (mtname, Memc[device], SZ_DEVICE, ofileno, orecno, + Memc[devcap], SZ_DEVCAP) + call mtencode (outstr, maxch, + Memc[device], fileno, orecno, Memc[devcap]) + + call sfree (sp) +end diff --git a/sys/mtio/mtglock.x b/sys/mtio/mtglock.x new file mode 100644 index 00000000..a1abfef5 --- /dev/null +++ b/sys/mtio/mtglock.x @@ -0,0 +1,47 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "mtio.h" + +# MT_GLOCK -- Return the lockfile name for the given magtape device +# specification. There can be many logical devices, defined in the tapecap +# file, which refer to the same physical device. All entries for the same +# physical device share the same i/o device file and lock file. If the +# physical device is on a remote node the tapecap file on that node should +# be accessed, and the node name must be included in the lockfile file name, +# although the lock file is always written on the local node. + +procedure mt_glock (mtname, lockfile, maxch) + +char mtname[ARB] #I full magtape device spec +char lockfile[ARB] #O receives lockfile name +int maxch #I max chars out + +int filno, recno +pointer sp, lkname, device, devcap, gty +errchk mt_gtyopen, syserrs +int gtygets() +pointer mt_gtyopen() + +begin + call smark (sp) + call salloc (lkname, SZ_FNAME, TY_CHAR) + call salloc (device, SZ_FNAME, TY_CHAR) + call salloc (devcap, SZ_DEVCAP, TY_CHAR) + + call mtparse (mtname, + Memc[device], SZ_FNAME, filno, recno, Memc[devcap], SZ_DEVCAP) + + # The "lk" capability specifies the lock file root name. + gty = mt_gtyopen (Memc[device], Memc[devcap]) + if (gtygets (gty, "lk", Memc[lkname], SZ_FNAME) <= 0) { + call eprintf ("missing `lk' parameter in tapecap entry for %s\n") + call pargstr (mtname) + call syserrs (SYS_MTTAPECAP, mtname) + } + + call ki_xnode (Memc[device], Memc[lkname], SZ_FNAME) + call mt_lockname (Memc[lkname], lockfile, maxch) + + call sfree (sp) +end diff --git a/sys/mtio/mtgtyopen.x b/sys/mtio/mtgtyopen.x new file mode 100644 index 00000000..489fba2d --- /dev/null +++ b/sys/mtio/mtgtyopen.x @@ -0,0 +1,129 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <syserr.h> +include "mtio.h" + +# MT_GTYOPEN -- Given a logical device name, open the tapecap file and extract +# the entry for the named device into an open GTY descriptor. The descriptor +# pointer is returned as the function value. The last entry accessed is +# cached indefinitely so that repeated references cause the tapecap file to +# be scanned only once. +# +# The tapecap file may be defined in the user environment, otherwise it +# defaults to the compiled in default TAPECAP (dev$tapecap). The format +# of the "tapecap" environment variable is "filename[:devcap]", e.g. +# +# home$tapecap:so +# +# +# would cause the file home$tapecap to be used as the tapecap file, adding the +# device capabilities :so to each tapecap device access (any devcap fields +# given on the command line will override these). Either the filename or +# devcap field may be omitted. For example, reset tapecap = ":so" causes +# the default tapecap file dev$tapecap to be used, but enables status output +# logging in each magtape access. + +pointer procedure mt_gtyopen (device, ufields) + +char device[ARB] #I local device name (incl node) +char ufields[ARB] #I optional user tapecap fields + +int len_ufields, junk +char c_device[SZ_DEVICE] +char c_ufields[SZ_DEVCAP] +bool first_time, capseen, remote +pointer c_gty, gty, sp, tapecap, devcap, ip, op +pointer fname, dname, nname, tname + +bool streq() +pointer gtyopen() +int envfind(), stridxs(), strlen(), gstrcpy(), ki_gnode() +errchk gtyopen, syserrs +data first_time /true/ + +begin + # First time initialization. + if (first_time) { + c_gty = NULL + first_time = false + } + + # Check the cache. + if (c_gty != NULL && streq(device,c_device) && streq(ufields,c_ufields)) + return (c_gty) + + # Cache miss. Free the old descriptor. + if (c_gty != NULL) + call gtyclose (c_gty) + + call smark (sp) + call salloc (tapecap, SZ_DEVCAP, TY_CHAR) + call salloc (devcap, SZ_DEVCAP, TY_CHAR) + call salloc (fname, SZ_PATHNAME, TY_CHAR) + call salloc (tname, SZ_PATHNAME, TY_CHAR) + call salloc (dname, SZ_FNAME, TY_CHAR) + call salloc (nname, SZ_FNAME, TY_CHAR) + + # Get tapecap definition. + if (envfind ("tapecap", Memc[tapecap], SZ_DEVCAP) <= 0) + call strcpy (TAPECAP, Memc[tapecap], SZ_DEVCAP) + + # Parse into filename and devcap fields. + op = fname + capseen = false + len_ufields = gstrcpy (ufields, Memc[devcap], SZ_DEVCAP) + for (ip=tapecap; Memc[ip] != EOS; ip=ip+1) { + if (Memc[ip] == ':' && !capseen) { + Memc[op] = EOS + op = devcap + len_ufields + capseen = true + } + Memc[op] = Memc[ip] + op = op + 1 + } + Memc[op] = EOS + + # Supply default filename if none given. + if (Memc[fname] == EOS) + call strcpy (TAPECAP, Memc[fname], SZ_PATHNAME) + + # If no node is specified for the tapecap file, access the tapecap + # file on the same node as the magtape device file. + + if (stridxs ("!", Memc[fname]) <= 0) + call ki_xnode (device, Memc[fname], SZ_PATHNAME) + + # Get the node name the device is on. + remote = (ki_gnode (Memc[fname], Memc[nname], junk) != 0) + + # Get the tapecap device name minus any node prefix. + call strcpy (device, Memc[dname], SZ_FNAME) + call ki_xnode ("", Memc[dname], SZ_FNAME) + + # Open the tapecap entry. Try "tapecap.<node>" first then "tapecap". + # <node> is the hostname of the host the tape device is on, i.e. the + # network server on which the drive is located. The "tapecap.<node>" + # feature allows a shared common IRAF installation to support distinct + # tapecap files for different servers, falling back on the standard + # tapecap file if no node-specific file is found. + + call strcpy (Memc[fname], Memc[tname], SZ_PATHNAME) + call strcat (".", Memc[tname], SZ_PATHNAME) + call strcat (Memc[nname], Memc[tname], SZ_PATHNAME) + + iferr (gty = gtyopen (Memc[tname], Memc[dname], Memc[devcap])) + iferr (gty = gtyopen (Memc[fname], Memc[dname], Memc[devcap])) + call syserrs (SYS_MTTAPECAP, device) + if (gty == NULL) + call syserrs (SYS_MTTAPECAP, device) + + # Update the cache. + if (strlen(ufields) <= SZ_DEVCAP) { + call strcpy (device, c_device, SZ_DEVICE) + call strcpy (Memc[devcap], c_ufields, SZ_DEVCAP) + c_gty = gty + } + + call sfree (sp) + return (gty) +end diff --git a/sys/mtio/mtio.com b/sys/mtio/mtio.com new file mode 100644 index 00000000..8accab3b --- /dev/null +++ b/sys/mtio/mtio.com @@ -0,0 +1,9 @@ +# The MTIO Common. + +int new_mtchan # flag newly opened channel +int mtdev[LEN_MTIODES,MT_MAXTAPES+1] # integer fields +char mtnam[SZ_DEVICE,MT_MAXTAPES+1] # array of drive names +char mtosn[SZ_IODEV,MT_MAXTAPES+1] # host name for device +char mtlkn[SZ_LKNAME,MT_MAXTAPES+1] # lock file name + +common /mtiocom/ new_mtchan, mtdev, mtnam, mtosn, mtlkn diff --git a/sys/mtio/mtio.h b/sys/mtio/mtio.h new file mode 100644 index 00000000..a400a88a --- /dev/null +++ b/sys/mtio/mtio.h @@ -0,0 +1,42 @@ +# MTIO.H -- Magtape i/o interface definitions. Note that the system config +# file contains additional definitions (i.e., MT_MAXTAPES). + +define TAPECAP "dev$tapecap" # default tapecap file +define LOCKLDIR "tmp$" # where the lock file goes +define LOCKFILE "mt" # root lockfile name +define LOCKEXTN ".lok" # lockfile extension +define MT_MAGIC (-5417) # was zopnmt called by mtopen? +define SZ_DEVICE 79 # max length of drive name +define SZ_IODEV 79 # max length host device name +define SZ_LKNAME 79 # max length lock file mame +define SZ_DEVCAP 512 # max command line tapecap chars + +# MTIO device descriptor structure. The device descriptor is implemented +# as the two dimensional integer array MTDEV, defined in the mtio common. +# The DEVPOS substructure must agree with the driver, os$zfiomt.c. + +define MT_DEVICE mtnam[1,$1+1] # drive name +define MT_IODEV mtosn[1,$1+1] # i/o device +define MT_LKNAME mtlkn[1,$1+1] # lock file name + +define LEN_MTIODES 11 +define MT_DEVPOS MT_FILNO # devpos struct (passed to driver) +define LEN_DEVPOS 5 + +define MT_OSCHAN mtdev[1,$1+1] # OS channel or 0 +define MT_ACMODE mtdev[2,$1+1] # new access mode +define MT_DEVCAP mtdev[3,$1+1] # pointer to tapecap entry for device +define MT_FILE mtdev[4,$1+1] # new file number +define MT_RECORD mtdev[5,$1+1] # new record number +define MT_ATEOF mtdev[6,$1+1] # reached end of file on a read +define MT_FILNO mtdev[7,$1+1] # old file number at open +define MT_RECNO mtdev[8,$1+1] # old record number at open +define MT_NFILES mtdev[9,$1+1] # nfiles on tape +define MT_TAPEUSED mtdev[10,$1+1] # total tape used, bytes +define MT_PFLAGS mtdev[11,$1+1] # i/o flags returned by driver + +# PFLAGS bitflags. +define MF_ERR 001B # i/o error in last operation +define MF_EOF 002B # tape mark seen in last operation +define MF_EOT 004B # end of tape seen in last op +define MF_EOR 010B # last op was a record advance diff --git a/sys/mtio/mtlocknam.x b/sys/mtio/mtlocknam.x new file mode 100644 index 00000000..0820645a --- /dev/null +++ b/sys/mtio/mtlocknam.x @@ -0,0 +1,40 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <ctype.h> +include <chars.h> +include "mtio.h" + +# MT_LOCKNAME -- Generate the file name of the magtape lock file, given the +# logical drive name. We are called from a z-routine, so do not use any high +# level i/o routines. The generated lockfile name is of the form +# +# [node!]dir$mta.lok +# +# The lock file is maintained on the same node as the drive to which it +# refers. + +procedure mt_lockname (device, lockfile, maxch) + +char device[ARB] #I device name +char lockfile[maxch] #O receives generated lockfile name +int maxch #I max chars out + +int ip, op +int gstrcpy(), strlen() + +begin + lockfile[1] = EOS + + # Copy the node name prefix, if any. + call ki_xnode (device, lockfile, maxch) + op = strlen (lockfile) + 1 + ip = op + + # Add the directory name prefix, "mt", and device name. + op = op + gstrcpy (LOCKLDIR, lockfile[op], maxch-op+1) + op = op + gstrcpy (LOCKFILE, lockfile[op], maxch-op+1) + op = op + gstrcpy (device[ip], lockfile[op], maxch-op+1) + + # Add file extension. + op = op + gstrcpy (LOCKEXTN, lockfile[op], maxch-op+1) +end diff --git a/sys/mtio/mtneedf.x b/sys/mtio/mtneedf.x new file mode 100644 index 00000000..9c334949 --- /dev/null +++ b/sys/mtio/mtneedf.x @@ -0,0 +1,26 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "mtio.h" + +# MTNEEDFILENO -- Returns YES if no file number is specified in mtname, or +# NO if a file is specified. + +int procedure mtneedfileno (mtname) + +char mtname[ARB] #I magtape device specification + +int fileno, recno +pointer sp, device, devcap +int btoi() + +begin + call smark (sp) + call salloc (device, SZ_DEVICE, TY_CHAR) + call salloc (devcap, SZ_DEVCAP, TY_CHAR) + + call mtparse (mtname, Memc[device], SZ_DEVICE, fileno, recno, + Memc[devcap], SZ_DEVCAP) + + call sfree (sp) + return (btoi(fileno == ERR)) +end diff --git a/sys/mtio/mtopen.x b/sys/mtio/mtopen.x new file mode 100644 index 00000000..fc5d6c4f --- /dev/null +++ b/sys/mtio/mtopen.x @@ -0,0 +1,188 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <knet.h> +include <config.h> +include <syserr.h> +include <fset.h> +include <mach.h> +include "mtio.h" + +# MTOPEN -- Open a magtape file, or a regular binary file if the file name is +# not "mt" prefixed. Access modes are restricted to read_only, write_only, +# append, and new_tape. The buffer size argument specifies the size of the +# FIO buffer used to access the tape; a system and device dependent default +# is supplied if the buffer size is given as zero. If the device is a fixed +# block size device the buffer size will be adjusted to an integral multiple +# of the device block size. For variable size record devices, the buffer size +# determines the size of the tape record on a write, or the maximum record +# size on a read. +# +# The device to be accessed is specified as follows: +# +# [node!] mtX [ '[' file[.record] [:attr-list] ']' ] +# +# for example, +# +# mtexb1[4:nb:se@:ts=1200:so=/dev/ttya8] +# +# The "mt" prefix is required for the object to be considered a magtape device +# reference. The device name returned is "mtX" as shown above; there must be +# an entry for device mtX in the tapecap file in DEV. +# +# The file and record numbers are optional. Files and records are numbered +# starting with 1. A sequence such as "mtX[eot]" will cause the tape to be +# positioned to end of tape. "mtX[0]" causes the tape to be opened at the +# current position, i.e., without being moved. +# +# The optional attr-list field consists of a sequence of colon-delimited +# tapecap fields. These will override any values given in the tapecap entry +# for the device. The syntax for attr-list is the same as in tapecap. +# +# If the filespec does not have the prefix "mt", we assume that the file is +# a regular binary file and try to open that. If a tape file is specified +# then the drive must be allocated before we are called. We allocate and +# initialize an MTIO file descriptor and call FOPNBF to install the magtape +# device in FIO and open the device/file. + +int procedure mtopen (mtname, acmode, bufsize) + +char mtname[ARB] #I device to be opened +int acmode #I access mode +int bufsize #I fio buffer size (record size) or 0 + +bool first_time +pointer sp, devcap, fname, gty +int mt, fd, nskip, new_file, new_record + +bool streq() +pointer mt_gtyopen(), gtycaps() +int open(), fopnbf(), gtygets(), access() +int mt_skip_record(), mtfile(), mt_devallocated() +extern zopnmt(), zardmt(), zawrmt(), zawtmt(), zsttmt(), zclsmt() + +errchk open, fopnbf, fseti, syserrs, mtparse +errchk mt_getpos, mt_skip_record, mt_gtyopen, gtygets, mt_glock, mtallocate +data first_time /true/ +include "mtio.com" + +begin + call smark (sp) + call salloc (fname, SZ_PATHNAME, TY_CHAR) + call salloc (devcap, SZ_DEVCAP, TY_CHAR) + + # Runtime initialization of the mtio file descriptor common. + # Make each file descriptor available for use. + + if (first_time) { + call mt_clrcache() + do mt = 1, MT_MAXTAPES + MT_OSCHAN(mt) = NULL + first_time = false + } + + # If regular binary file, we are done. + if (mtfile(mtname) == NO) { + call sfree (sp) + return (open (mtname, acmode, BINARY_FILE)) + } + + # Get mtio file descriptor slot, but do not allocate it until + # we are ready to open the file. + + for (mt=1; mt <= MT_MAXTAPES && MT_OSCHAN(mt) != NULL; mt=mt+1) + ; + if (mt > MT_MAXTAPES) + call syserrs (SYS_MTMULTOPEN, mtname) + + # Break mtname into drive name, file and record number, etc. + call mtparse (mtname, MT_DEVICE(mt), SZ_DEVICE, + new_file, new_record, Memc[devcap], SZ_DEVCAP) + if (new_record == ERR) + new_record = 1 + + # Get tapecap info. + gty = mt_gtyopen (MT_DEVICE(mt), Memc[devcap]) + MT_DEVCAP(mt) = gtycaps (gty) + if (gtygets (gty, "dv", MT_IODEV(mt), SZ_IODEV) <= 0) { + call eprintf ("missing `dv' parameter in tapecap entry for %s\n") + call pargstr (mtname) + call syserrs (SYS_MTTAPECAP, mtname) + } + call ki_xnode (MT_DEVICE(mt), MT_IODEV(mt), SZ_IODEV) + + # If the device has not been allocated, at least write out the + # lock file. This will not physically allocate the device, but + # the lock file is required to be able to access the device. + + call mt_glock (mtname, MT_LKNAME(mt), SZ_LKNAME) + if (mt_devallocated (MT_IODEV(mt)) == NO) + if (access (MT_LKNAME(mt), 0,0) == NO) + call mtallocate (mtname) + + # Get current tape position. + call mt_getpos (mtname, mt) + + MT_FILE(mt) = new_file + MT_RECORD(mt) = new_record + MT_ATEOF(mt) = NO + + # If tape is opened for writing but no file number is given, default + # to EOT. Defaulting to current file or BOT could result in + # destruction of the tape. Note that this default WILL RESULT IN TAPE + # RUNAWAY if used on a blank tape. Blank tapes must be explicitly + # written at file [1], or opened with access mode NEW_TAPE. + + if ((acmode == WRITE_ONLY && MT_FILE(mt) == -1) || (acmode == APPEND)) { + MT_FILE(mt) = EOT + MT_RECORD(mt) = 1 + } else if (acmode == NEW_TAPE) { + MT_FILE(mt) = 1 + MT_RECORD(mt) = 1 + } + + # Make sure that we are not reopening a drive which is already open. + for (fd=1; fd <= MT_MAXTAPES; fd=fd+1) + if (fd != mt && MT_OSCHAN(fd) != NULL) + if (streq (MT_DEVICE(fd), MT_DEVICE(mt))) + call syserrs (SYS_MTMULTOPEN, mtname) + + # Initialize the remaining fields in the file descriptor and open the + # device. ZOPNMT will position the tape. Note that we pass the index + # of the new mtio descriptor slot to ZOPNMT in the common. This is a + # bit ugly, but is safe enough, since we know that FOPNBF is going to + # call ZOPNMT. + + switch (acmode) { + case READ_ONLY: + MT_ACMODE(mt) = READ_ONLY + case WRITE_ONLY, APPEND, NEW_TAPE: + MT_ACMODE(mt) = WRITE_ONLY + default: + call syserrs (SYS_MTACMODE, mtname) + } + + new_mtchan = mt + fd = fopnbf (MT_IODEV(mt), acmode, + zopnmt, zardmt, zawrmt, zawtmt, zsttmt, zclsmt) + + # Set the file buffer size (record size for variable block devices). + if (bufsize > 0) + call fseti (fd, F_BUFSIZE, bufsize) + + # If the user specified a record offset, skip records up to there. + # Zero means leave positioned to old record. + + if (MT_RECORD(mt) == 0) + MT_RECORD(mt) = MT_RECNO(mt) + if (MT_RECORD(mt) > 1) { + nskip = MT_RECORD(mt) - 1 + MT_RECORD(mt) = 1 + if (mt_skip_record (fd, nskip) != nskip) + call syserrs (SYS_MTSKIPREC, mtname) + } + + call mt_savepos (mt) + + call sfree (sp) + return (fd) +end diff --git a/sys/mtio/mtparse.x b/sys/mtio/mtparse.x new file mode 100644 index 00000000..27622280 --- /dev/null +++ b/sys/mtio/mtparse.x @@ -0,0 +1,126 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <config.h> +include <syserr.h> +include <ctype.h> +include "mtio.h" + +# MTPARSE -- Decode a virtual magtape file specification, returning the device +# name, file and record to which the drive is to be positioned, and any special +# device attributes (these will override the device defaults). The file and +# record fields are returned as ERR if missing. Only the drive name field is +# required. +# +# Magtape device syntax: +# +# [node!] mtX [ '[' file[.record] [:attr-list] ']' ] +# +# for example, +# +# mtexb1[4:nb:se@:ts=1200:so=/dev/ttya8] +# +# The "mt" prefix is required for the object to be considered a magtape +# device reference. The device name returned is "mtX" as shown above; there +# must be an entry for device mtX in the tapecap file in DEV. +# +# The file and record numbers are optional. Files and records are numbered +# starting with 1. A sequence such as "mtX[eot]" will cause the tape to be +# positioned to end of tape. "mtX[0]" causes the tape to be opened at the +# current position, i.e., without being moved. +# +# The optional attr-list field consists of a sequence of colon-delimited +# tapecap fields. These will override any values given in the tapecap +# entry for the device. The syntax for attr-list is the same as in tapecap. + +procedure mtparse (mtname, device, sz_device, file, record, attrl, sz_attrl) + +char mtname[ARB] #I device specification +char device[ARB] #O device name as in tapecap +int sz_device #I max chars in device name +int file #O file number or -1 +int record #O record number or -1 +char attrl[ARB] #O attribute list +int sz_attrl #I max char in attribute list + +char eotstr[3] +int ip, op, nchars, ival +int ctoi(), strncmp(), ki_extnode() +bool streq() +define bad_ 91 + +begin + # Extract the node name, if any, from the mtname. + ip = ki_extnode (mtname, device, sz_device, nchars) + 1 + op = nchars + 1 + + # Verify that this is a magtape device specification. + if (strncmp (mtname[ip], "mt", 2) != 0) + goto bad_ + + # Extract the device name field. + while (mtname[ip] != EOS && mtname[ip] != '[') { + device[op] = mtname[ip] + op = min (sz_device, op + 1) + ip = ip + 1 + } + device[op] = EOS + + file = ERR + record = ERR + attrl[1] = EOS + + # Process the [...] part of the device specification. + if (mtname[ip] == '[') { + ip = ip + 1 + + # Get the file number. + if (ctoi (mtname, ip, ival) > 0) { + file = ival + if (file < 0) + goto bad_ + } else if (IS_ALPHA(mtname[ip])) { + call strcpy (mtname[ip], eotstr, 3) + call strlwr (eotstr) + if (streq (eotstr, "eot")) { + file = EOT + ip = ip + 3 + } else + goto bad_ + } + + # Get the record number. + if (mtname[ip] == '.' || mtname[ip] == ',') { + ip = ip + 1 + if (mtname[ip] == ']') + record = ERR + else if (ctoi (mtname, ip, ival) > 0) { + record = ival + if (record < 0) + goto bad_ + } + } + + # Get the device attribute list. + op = 1 + if (mtname[ip] == ':') { + attrl[op] = mtname[ip] + op = max(1, min(sz_attrl, op + 1)) + ip = ip + 1 + + while (mtname[ip] != EOS && mtname[ip] != ']') { + attrl[op] = mtname[ip] + op = max(1, min(sz_attrl, op + 1)) + ip = ip + 1 + } + } + attrl[op] = EOS + + # Check for the ']' terminator. + if (mtname[ip] != ']') + goto bad_ + } + + return +bad_ + call syserrs (SYS_MTFILSPEC, mtname) +end diff --git a/sys/mtio/mtpos.x b/sys/mtio/mtpos.x new file mode 100644 index 00000000..f88946ae --- /dev/null +++ b/sys/mtio/mtpos.x @@ -0,0 +1,39 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include "mtio.h" + +# MTPOSITION -- Position the device to the indicated file and record. +# We are called to position the device by device name, not to position +# an open magtape file. + +procedure mtposition (mtname, file, record) + +char mtname[ARB] #I device to be positioned +int file #I desired file number +int record #I desired record number + +int junk +pointer sp, mtspec, device, devcap +errchk mtparse, mtopen +int mtopen() + +begin + call smark (sp) + call salloc (device, SZ_FNAME, TY_CHAR) + call salloc (mtspec, SZ_FNAME, TY_CHAR) + call salloc (devcap, SZ_DEVCAP, TY_CHAR) + + # Get device name (including node! prefix) from mtname. + call mtparse (mtname, + Memc[device], SZ_FNAME, junk, junk, Memc[devcap], SZ_DEVCAP) + + # Encode new mtname and open device to position to desired file. + # Note that we do not return until positioning is complete. Thus, + # "mtposition(device,1)" is a rewind with wait. + + call mtencode (Memc[mtspec], SZ_FNAME, + Memc[device], file, record, Memc[devcap]) + call close (mtopen (Memc[mtspec], READ_ONLY, 1)) + + call sfree (sp) +end diff --git a/sys/mtio/mtrdlock.x b/sys/mtio/mtrdlock.x new file mode 100644 index 00000000..774fab19 --- /dev/null +++ b/sys/mtio/mtrdlock.x @@ -0,0 +1,93 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <config.h> +include "mtio.h" + +# MT_READ_LOCKFILE -- Read the magtape lock file to determine the current +# position of the tape (the lock file is used to record the tape position +# while the device is closed). If the lock file cannot be accessed or an +# error occurs in reading it, return an undefined tape position. + +procedure mt_read_lockfile (mtname, mt) + +char mtname[ARB] #I device name +int mt #I MTIO descriptor + +int fd, ip +pointer sp, lockfile, lbuf +int strmatch(), stridxs(), ctoi(), open(), getline() +errchk open, getline +include "mtio.com" +define err_ 91 + +begin + call smark (sp) + call salloc (lockfile, SZ_FNAME, TY_CHAR) + call salloc (lbuf, SZ_LINE, TY_CHAR) + + # If the lock file cannot be accessed, return an undefined tape + # position but do not abort. + + #call mt_lockname (MT_LKNAME(mt), Memc[lockfile], SZ_FNAME) + call strcpy (MT_LKNAME(mt), Memc[lockfile], SZ_FNAME) + iferr (fd = open (Memc[lockfile], READ_ONLY, TEXT_FILE)) { + fd = ERR + goto err_ + } + + # Get file number. + repeat { + if (getline (fd, Memc[lbuf]) == EOF) + goto err_ + } until (strmatch (Memc[lbuf], "^file") > 0) + ip = stridxs ("=", Memc[lbuf]) + 1 + if (ctoi (Memc[lbuf], ip, MT_FILNO(mt)) == 0) + goto err_ + + # Get record number. + if (getline (fd, Memc[lbuf]) == EOF) + goto err_ + ip = stridxs ("=", Memc[lbuf]) + 1 + if (ctoi (Memc[lbuf], ip, MT_RECNO(mt)) == 0) + goto err_ + + # Get total files on tape. + if (getline (fd, Memc[lbuf]) == EOF) + goto err_ + ip = stridxs ("=", Memc[lbuf]) + 1 + if (ctoi (Memc[lbuf], ip, MT_NFILES(mt)) == 0) + goto err_ + + # Get amount of tape used. + if (getline (fd, Memc[lbuf]) == EOF) + goto err_ + ip = stridxs ("=", Memc[lbuf]) + 1 + if (ctoi (Memc[lbuf], ip, MT_TAPEUSED(mt)) == 0) + goto err_ + + # Get pflags. + if (getline (fd, Memc[lbuf]) == EOF) + goto err_ + ip = stridxs ("=", Memc[lbuf]) + 1 + if (ctoi (Memc[lbuf], ip, MT_PFLAGS(mt)) == 0) + goto err_ + + call close (fd) + call sfree (sp) + + return +err_ + if (fd != ERR) + call close (fd) + + # Write a new lock file so that we can update the tape position + # later (the file must exist after the drive is opened). + + call mtallocate (mtname) + + # Return an undefined tape position. + MT_FILNO(mt) = -1 + MT_RECNO(mt) = -1 + + call sfree (sp) +end diff --git a/sys/mtio/mtrewind.x b/sys/mtio/mtrewind.x new file mode 100644 index 00000000..b7efcddb --- /dev/null +++ b/sys/mtio/mtrewind.x @@ -0,0 +1,41 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <error.h> +include "mtio.h" + +# MTREWIND -- Rewind the named magtape device. This is a synchronous +# rewind. Rewind not only rewinds the device, it also initializes the +# MTIO view of what is on the tape (number of files, total bytes used). +# Hence, if the drive is left allocated but the tape is changed, or if +# the position cache becomes inaccurate for any reason, a rewind will +# initialize things without having to deallocate and reallocate the drive. + +procedure mtrewind (mtname, initcache) + +char mtname[ARB] #I device to be rewound +int initcache #I discard positional information? + +pointer sp, fname +int fd, mtopen() +errchk mtfname + +begin + call smark (sp) + call salloc (fname, SZ_FNAME, TY_CHAR) + + # Init position cache. + if (initcache == YES) { + call mt_glock (mtname, Memc[fname], SZ_FNAME) + iferr (call delete (Memc[fname])) + ; + } + + # Rewind device. + call mtfname (mtname, 1, Memc[fname], SZ_FNAME) + iferr (fd = mtopen (Memc[fname], READ_ONLY, 0)) + call erract (EA_WARN) + else + call close (fd) + + call sfree (sp) +end diff --git a/sys/mtio/mtskip.x b/sys/mtio/mtskip.x new file mode 100644 index 00000000..f51948a4 --- /dev/null +++ b/sys/mtio/mtskip.x @@ -0,0 +1,31 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <fset.h> + +# MT_SKIP_RECORD -- Skip records on an opened file. Return the actual number +# of records skipped; stop if EOF is reached. + +int procedure mt_skip_record (fd, nrecords) + +int fd #I magtape device +int nrecords #I number of records to skip + +pointer sp, buf +int n, bufsize +errchk aread, await +int await(), fstati() + +begin + call smark (sp) + bufsize = fstati (fd, F_BUFSIZE) + call salloc (buf, bufsize, TY_CHAR) + + for (n=1; n <= nrecords; n=n+1) { + call aread (fd, Memc[buf], bufsize, 0) + if (await (fd) == EOF) + break + } + + call sfree (sp) + return (n-1) +end diff --git a/sys/mtio/mtstatus.x b/sys/mtio/mtstatus.x new file mode 100644 index 00000000..967d25df --- /dev/null +++ b/sys/mtio/mtstatus.x @@ -0,0 +1,34 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +# MTSTATUS -- Print the status of an allocated magtape device, i.e., print the +# lock file as a text file. Called by the DEVSTATUS task. + +procedure mtstatus (out, mtname) + +int out #I output file +char mtname[ARB] #I magtape specification + +int in +pointer sp, lockfile +errchk open, fcopyo +int open(), access() + +begin + call smark (sp) + call salloc (lockfile, SZ_FNAME, TY_CHAR) + + call mt_sync (OK) + + call mt_glock (mtname, Memc[lockfile], SZ_FNAME) + if (access (Memc[lockfile], 0, 0) == NO) { + call fprintf (out, "tape position for %s is undefined\n") + call pargstr (mtname) + } else { + # Print the lockfile. + in = open (Memc[lockfile], READ_ONLY, TEXT_FILE) + call fcopyo (in, out) + call close (in) + } + + call sfree (sp) +end diff --git a/sys/mtio/mtupdlock.x b/sys/mtio/mtupdlock.x new file mode 100644 index 00000000..654d3cbc --- /dev/null +++ b/sys/mtio/mtupdlock.x @@ -0,0 +1,188 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <knet.h> +include <mach.h> +include <config.h> +include "mtio.h" + +# MT_UPDATE_LOCKFILE -- Update the current file position in the lockfile. +# This information is returned by the host specific driver at close time. +# We are called from a z-routine so we must access the lockfile using only +# low level OS interface routines to avoid recursion. We may be called during +# error recovery (as well as from a z-routine), so any errors are fatal. + +procedure mt_update_lockfile (mt) + +int mt #I device slot + +extern mt_sync() +pointer sp, lockfile, tempfile, lbuf, ip, op, extn +int old_lockfile, new_lockfile, junk, status, nlines +errchk fmapfn +include "mtio.com" +define oline_ 91 +define err_ 92 + +begin + call smark (sp) + call salloc (lockfile, SZ_PATHNAME, TY_CHAR) + call salloc (tempfile, SZ_PATHNAME, TY_CHAR) + call salloc (lbuf, SZ_LINE, TY_CHAR) + + # Catch any errors in the following section and convert them + # into fatal errors. + + iferr { + # Try to avoid generating any non-host legal filenames in the + # following code, to avoid any need to access the VFN mapping + # file. Generate temp file in the same directory on the same + # node as the lockfile so that we can easily rename the tempfile + # to be the new lockfile. + + #call mt_lockname (MT_LKNAME(mt), Memc[lockfile], SZ_PATHNAME) + call strcpy (MT_LKNAME(mt), Memc[lockfile], SZ_PATHNAME) + + # Make tempfile name. + extn = NULL + op = tempfile + for (ip=lockfile; Memc[ip] != EOS; ip=ip+1) { + if (Memc[ip] == '.') + extn = op + Memc[op] = Memc[ip] + op = op + 1 + } + + if (extn == NULL) + extn = op + call strcpy (".tlk", Memc[extn], 4) + + # Map the filenames. + call fmapfn (Memc[tempfile], Memc[tempfile], SZ_PATHNAME) + call fmapfn (Memc[lockfile], Memc[lockfile], SZ_PATHNAME) + + } then + goto err_ + + # Overwrite any existing tempfile. + call zfdele (Memc[tempfile], junk) + call zopntx (Memc[tempfile], NEW_FILE, new_lockfile) + if (new_lockfile == ERR) + goto err_ + + # Open old lockfile, if any, and copy the comments section. + call zopntx (Memc[lockfile], READ_ONLY, old_lockfile) + if (old_lockfile == ERR) { +oline_ call strcpy ("# Magtape unit ", Memc[lbuf], SZ_LINE) + call strcat (MT_DEVICE(mt), Memc[lbuf], SZ_LINE) + call strcat (" status\n", Memc[lbuf], SZ_LINE) + call mt_putline (new_lockfile, Memc[lbuf]) + } else { + nlines = 0 + repeat { + call zgettx (old_lockfile, Memc[lbuf], SZ_LINE, status) + if (status <= 0) { + if (nlines == 0) { + call zclstx (old_lockfile, status) + goto oline_ + } else + break + } else + nlines = nlines + 1 + Memc[lbuf+status] = EOS + if (Memc[lbuf] == '#') + call mt_putline (new_lockfile, Memc[lbuf]) + } until (Memc[lbuf] != '#') + call zclstx (old_lockfile, status) + } + + # Everything else we write from here on is new stuff. Discard rest + # of old lockfile. + + # Save current file and record numbers. + if (MT_FILNO(mt) == -1) + MT_RECNO(mt) = -1 + call mt_savekeyword (new_lockfile, "file", MT_FILNO(mt)) + if (MT_NFILES(mt) > 0 && (MT_FILNO(mt) == MT_NFILES(mt) + 1)) + call mt_putline (new_lockfile, " (EOT)") + call mt_putline (new_lockfile, "\n") + call mt_savekeyword (new_lockfile, "record", MT_RECNO(mt)) + call mt_putline (new_lockfile, "\n") + call mt_savekeyword (new_lockfile, "nfiles", MT_NFILES(mt)) + call mt_putline (new_lockfile, "\n") + call mt_savekeyword (new_lockfile, "tapeused", MT_TAPEUSED(mt)) + call mt_putline (new_lockfile, "\n") + call mt_savekeyword (new_lockfile, "pflags", MT_PFLAGS(mt)) + call mt_putline (new_lockfile, "\n") + + # Install the new lockfile. + call zflstx (new_lockfile, status) + if (status == ERR) + goto err_ + call zclstx (new_lockfile, status) + if (status == ERR) + goto err_ + + call zfdele (Memc[lockfile], status) + call zfrnam (Memc[tempfile], Memc[lockfile], status) + if (status == ERR) + goto err_ + + call sfree (sp) + return + +err_ + # If an error of any sort occurs, it is fatal. + call onerror_remove (mt_sync) + call zfdele (Memc[tempfile], status) + call zfdele (Memc[lockfile], status) + call fatal (0, "Fatal error writing magtape device lockfile") +end + + +# MT_SAVEKEYWORD -- Write a "keyword = value" status line into the lockfile. + +procedure mt_savekeyword (fd, keyword, value) + +int fd # output file +char keyword[ARB] # name of keyword +int value # value of keyword +char numbuf[MAX_DIGITS] +int junk, itoc() + +begin + junk = itoc (value, numbuf, MAX_DIGITS) + + call mt_putline (fd, keyword) + call mt_putline (fd, " = ") + call mt_putline (fd, numbuf) +end + + +# MT_PUTLINE -- Put a text string to the lockfile. Do not write line +# to lockfile until a newline is seen. + +procedure mt_putline (fd, text) + +int fd +char text[ARB] + +extern mt_sync() +char lbuf[SZ_LINE] +int ip, op, status +data op /1/ + +begin + for (ip=1; text[ip] != EOS; ip=ip+1) { + lbuf[op] = text[ip] + op = min (SZ_LINE, op) + 1 + if (text[ip] == '\n') { + call zputtx (fd, lbuf, op-1, status) + if (status == ERR) { + call onerror_remove (mt_sync) + call fatal (0, + "Fatal error writing magtape device lockfile") + } + op = 1 + } + } +end diff --git a/sys/mtio/zardmt.x b/sys/mtio/zardmt.x new file mode 100644 index 00000000..ac5833be --- /dev/null +++ b/sys/mtio/zardmt.x @@ -0,0 +1,22 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <knet.h> +include <config.h> +include "mtio.h" + +# ZARDMT -- MTIO asynchronous read primitive. Initiate a read of up to +# maxbytes bytes into the user buffer. + +procedure zardmt (mtchan, buf, maxbytes, offset) + +int mtchan #I i/o channel +char buf[ARB] #O output data buffer +int maxbytes #I max bytes to read +long offset #I file offset + +include "mtio.com" + +begin + if (MT_ATEOF(mtchan) == NO) + call zzrdmt (MT_OSCHAN(mtchan), buf, maxbytes, offset) +end diff --git a/sys/mtio/zawrmt.x b/sys/mtio/zawrmt.x new file mode 100644 index 00000000..5caf2b70 --- /dev/null +++ b/sys/mtio/zawrmt.x @@ -0,0 +1,21 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <knet.h> +include <config.h> +include "mtio.h" + +# ZAWRMT -- MTIO asynchronous write primitive. Initiate a write of nbytes +# bytes to the tape. + +procedure zawrmt (mtchan, buf, nbytes, offset) + +int mtchan #I i/o channel +char buf[ARB] #I data to be written +int nbytes #I number of bytes of data +long offset #I file offset + +include "mtio.com" + +begin + call zzwrmt (MT_OSCHAN(mtchan), buf, nbytes, offset) +end diff --git a/sys/mtio/zawtmt.x b/sys/mtio/zawtmt.x new file mode 100644 index 00000000..5599a81e --- /dev/null +++ b/sys/mtio/zawtmt.x @@ -0,0 +1,30 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <knet.h> +include <config.h> +include "mtio.h" + +# ZAWTMT -- Wait for the last i/o transfer to complete, update tape position +# counters, return nbytes|status to caller. + +procedure zawtmt (mtchan, status) + +int mtchan #I i/o channel +int status #O status (nbytes transferred or ERR) + +include "mtio.com" + +begin + # The "sticky" EOF should not be necessary but is needed due to the + # way FIO behaves when it hits EOF on a blocked file. In some + # circumstances (depends upon the file length) two reads are made and + # if the second read does not return zero EOF will not be detected. + + if (MT_ATEOF(mtchan) == YES) + status = 0 + else { + call zzwtmt (MT_OSCHAN(mtchan), MT_DEVPOS(mtchan), status) + if (status == 0) + MT_ATEOF(mtchan) = YES + } +end diff --git a/sys/mtio/zclsmt.x b/sys/mtio/zclsmt.x new file mode 100644 index 00000000..7c4dcbf7 --- /dev/null +++ b/sys/mtio/zclsmt.x @@ -0,0 +1,55 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <config.h> +include <knet.h> +include "mtio.h" + +# ZCLSMT -- Close a magtape file and device. Update lockfile so that we +# will know where the tape is positioned next time we open the device. +# Deallocate the mtio descriptor so that it may be reused again. +# +# We are being called during error recovery if "new_mtchan" is not null. +# If MT_OSCHAN has also been set, then ZZOPMT was interrupted, probably while +# trying to position the tape, and the position of the tape is indefinite. +# Close the tape with acmode=read so that no tape marks are written, and write +# the lockfile with file = -1 to signify that the position is indefinite. + +procedure zclsmt (mtchan, status) + +int mtchan #I i/o channel +int status #O close status + +int mt +bool error_recovery +include "mtio.com" + +begin + # Called by error recovery while positioning tape? (during open) + if (new_mtchan != NULL) { + mt = new_mtchan + if (MT_OSCHAN(mt) != NULL) + call zzclmt (MT_OSCHAN(mt), MT_DEVPOS(mt), status) + + call mt_savepos (mt) + call mt_sync (ERR) + new_mtchan = NULL + + } else { + mt = mtchan + + # If a task aborts while a tape file is open, mt_sync will + # already have been called to update the position, + # and the current file will have been set to undefined (-1). + + error_recovery = (MT_FILNO(mt) == -1) + + # Close device. This clobbers MT_FILNO (see above). + call zzclmt (MT_OSCHAN(mt), MT_DEVPOS(mt), status) + + # Update the tape position if not recovering from an abort. + if (!error_recovery) + call mt_savepos (mt) + } + + MT_OSCHAN(mt) = NULL +end diff --git a/sys/mtio/zopnmt.x b/sys/mtio/zopnmt.x new file mode 100644 index 00000000..1c55e1ae --- /dev/null +++ b/sys/mtio/zopnmt.x @@ -0,0 +1,58 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <knet.h> +include <config.h> +include "mtio.h" + +# ZOPNMT -- Open magtape device at the specifed file. We are called indirectly +# by MTOPEN (via fopnbf), which sets up a new mtio device decriptor pointed +# to by NEW_MTCHAN, and passes it via the mtio common. + +procedure zopnmt (iodev, acmode, mtchan) + +char iodev[ARB] #I PACKED i/o device name string +int acmode #I file access mode +int mtchan #O return value (mt descriptor index) + +int mt +pointer sp, pk_devcap +include "mtio.com" +define err_ 91 + +begin + call smark (sp) + call salloc (pk_devcap, SZ_DEVCAP, TY_CHAR) + + # Pick up index of mt descriptor slot set up by MTOPEN. Make sure + # that we were called by MTOPEN and not somebody else. + + mt = new_mtchan + if (mt < 1 || mt > MT_MAXTAPES) + goto err_ + + # Open the device. + call strpak (Memc[MT_DEVCAP(mt)], Memc[pk_devcap], SZ_DEVCAP) + call zzopmt (iodev, MT_ACMODE(mt), Memc[pk_devcap], MT_DEVPOS(mt), + MT_FILE(mt), MT_OSCHAN(mt)) + if (MT_OSCHAN(mt) == ERR) + goto err_ + + # If "new_mtchan" is nonzero when ZCLSMT is called, it implies that + # CLOSE was called during error recovery due to an interrupt of ZZOPMT + # and the position of the tape is undefined. Clear the flag since the + # open is now complete and we were not interrupted. + + new_mtchan = NULL + MT_FILNO(mt) = MT_FILE(mt) + call mt_savepos (mt) + + mtchan = mt + call sfree (sp) + return + +err_ + # Z-routines can only return ERR in the event of an error. + MT_OSCHAN(mt) = NULL + call sfree (sp) + mtchan = ERR +end diff --git a/sys/mtio/zsttmt.x b/sys/mtio/zsttmt.x new file mode 100644 index 00000000..252c9735 --- /dev/null +++ b/sys/mtio/zsttmt.x @@ -0,0 +1,21 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <knet.h> +include <mach.h> +include <config.h> +include <fio.h> +include "mtio.h" + +# ZSTTMT -- Get magtape device or device driver parameters and settings. + +procedure zsttmt (mtchan, what, lvalue) + +int mtchan #I mtio descriptor +int what #I status parameter to be returned +long lvalue #O returned status value + +include "mtio.com" + +begin + call zzstmt (MT_OSCHAN(mtchan), what, lvalue) +end diff --git a/sys/mtio/zzdebug.x b/sys/mtio/zzdebug.x new file mode 100644 index 00000000..cf02e610 --- /dev/null +++ b/sys/mtio/zzdebug.x @@ -0,0 +1,357 @@ +# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + +include <xalloc.h> +include <mach.h> +include <fset.h> + +task alloc = t_allocate, + dealloc = t_deallocate, + status = t_status, + mtpos = t_mtposition, + wtestfile = t_wtestfile, + mtexamine = t_mtexamine, + mtcopy = t_mtcopy, + rew = t_rewind + + +.help testmtio +.nf __________________________________________________________________________ +MTIO test routines. Assorted routines for verification of MTIO. + + alloc Allocate a drive. + + dealloc Deallocate a drive. + + status Print drive status + + mtpos Position to the indicated file and record. + + wtestfile Writes a test file. The number of records and the + range of record sizes may be specified. The contents + of a record are determined by its size. + + mtexamine Examines the structure of a tape. Tells the number of + files on the tape, the number of records in each file, + the sizes of the records, and optionally dumps the + contents of an indicated range of records from each + file. + + mtcopy Fast binary copy. Copies a binary disk or tape file + to a binary disk or tape file using all the FIO + defaults. +.endhelp _____________________________________________________________________ + + +# ALLOCATE -- Allocate a drive. + +procedure t_allocate() + +int junk, status +char drive[SZ_FNAME] +char owner[SZ_FNAME] +int xallocate(), xdevowner() + +begin + call clgstr ("drive", drive, SZ_FNAME) + status = xallocate (drive) + + switch (status) { + case OK: + call printf ("device allocated successfully\n") + case ERR: + call printf ("cannot allocate device\n") + case DV_DEVFREE: + call printf ("device is free and may be allocated\n") + case DV_DEVALLOC: + call printf ("device is already allocated\n") + case DV_DEVINUSE: + junk = xdevowner ("drive", owner, SZ_FNAME) + call printf ("device is already allocated to `%s'\n") + call pargstr (owner) + case DV_DEVNOTFOUND: + call printf ("device not found\n") + default: + call printf ("unknown status %d\n") + call pargi (status) + } +end + + +# DEALLOCATE -- Deallocate a drive. + +procedure t_deallocate() + +int junk, status +char drive[SZ_FNAME] +char owner[SZ_FNAME] + +bool clgetb() +int xdeallocate(), xdevowner() + +begin + call clgstr ("drive", drive, SZ_FNAME) + status = xdeallocate (drive, clgetb ("rewind")) + + switch (status) { + case OK: + call printf ("device deallocated successfully\n") + case ERR: + call printf ("cannot deallocate device\n") + case DV_DEVFREE: + call printf ("device is free and may be allocated\n") + case DV_DEVALLOC: + call printf ("device is already allocated\n") + case DV_DEVINUSE: + junk = xdevowner ("drive", owner, SZ_FNAME) + call printf ("device is already allocated to `%s'\n") + call pargstr (owner) + case DV_DEVNOTFOUND: + call printf ("device not found\n") + default: + call printf ("unknown status %d\n") + call pargi (status) + } +end + + +# STATUS -- Print drive status. + +procedure t_status() + +int status +char drive[SZ_FNAME] +char owner[SZ_FNAME] +int xdevowner() + +begin + call clgstr ("drive", drive, SZ_FNAME) + status = xdevowner (drive, owner, SZ_FNAME) + + switch (status) { + case OK: + call printf ("device deallocated successfully\n") + case ERR: + call printf ("cannot deallocate device\n") + case DV_DEVFREE: + call printf ("device is free and may be allocated\n") + case DV_DEVALLOC: + call printf ("device is already allocated\n") + case DV_DEVINUSE: + call printf ("device is allocated to `%s'\n") + call pargstr (owner) + case DV_DEVNOTFOUND: + call printf ("device not found\n") + default: + call printf ("unknown status %d\n") + call pargi (status) + } +end + + +# MTPOS -- Position to the indicated file and record. + +procedure t_mtposition() + +char drive[SZ_FNAME] +int clgeti() + +begin + call clgstr ("drive", drive, SZ_FNAME) + call mtposition (drive, clgeti("file"), clgeti("record")) +end + + +# WTESTFILE -- Write a test file to the tape. Specify file [1] to write to +# a new tape. If no file number is given, the file is appended to the tape. +# Specify the number of records to be written and the range of sizes in bytes +# of the records. Each byte of a record will contain the size of the record +# modulus 256. + +procedure t_wtestfile() + +char mtname[SZ_FNAME] +int nrecords +int min_recsize, max_recsize + +pointer buf +long seed +int fd, i, recsize, oschan, status +int clgeti(), mtopen(), fstati() +real urand() +data seed /123/ + +begin + # Get tapefile name and open file for writing. + call clgstr ("mtname", mtname, SZ_FNAME) + fd = mtopen (mtname, WRITE_ONLY, 1) + oschan = fstati (fd, F_CHANNEL) + + nrecords = max (0, clgeti ("nrecords")) + min_recsize = max (1, clgeti ("min_recsize")) + max_recsize = max (min_recsize, clgeti ("max_recsize")) + + call calloc (buf, max_recsize, TY_CHAR) + + # Records are written by directly calling ZAWRMT, so that we can + # write odd size records. + + do i = 1, nrecords { + recsize = int ((max_recsize - min_recsize) * urand (seed)) + + min_recsize + call zawrmt (oschan, Memc[buf], recsize, 0) + call zawtmt (oschan, status) + if (status == ERR) + call error (1, "write error") + } + + call mfree (buf, TY_CHAR) + call close (fd) +end + + +# MTEXAMINE -- Examine the structure of a tape filesystem or a file. If no file +# number is given, all files are examined. + +procedure t_mtexamine() + +int fileno, nrecords +char mtname[SZ_FNAME], mtfile[SZ_FNAME] +int strlen(), mt_examine() + +begin + call clgstr ("mtname", mtname, SZ_FNAME) + call fseti (STDOUT, F_FLUSHNL, YES) + + if (mtname[strlen(mtname)] == ']') { + call strcpy (mtname, mtfile, SZ_FNAME) + nrecords = mt_examine (STDOUT, mtname) + + } else { + fileno = 1 + repeat { + call sprintf (mtfile, SZ_FNAME, "%s[%d]") + call pargstr (mtname) + call pargi (fileno) + fileno = fileno + 1 + } until (mt_examine (STDOUT, mtfile) == 0) + } +end + + +# MT_EXAMINE -- Examine a magtape file. Print file number, then count +# successive records. When the record size changes, print the number of +# records encountered with the old size. When all done, print the total +# number of records and bytes. Return the number of records in the file. + +int procedure mt_examine (out, mtfile) + +int out # output stream +char mtfile[ARB] # magtape file to be examined + +pointer buf +int in, nrecords, totrecords, totbytes, bufsize, recsize, last_recsize +errchk mtopen, read, fstati, printf, pargi +int mtopen(), read(), fstati() + +begin + in = mtopen (mtfile, READ_ONLY, 0) + bufsize = fstati (in, F_BUFSIZE) + call malloc (buf, bufsize, TY_CHAR) + + call fprintf (out, " File %s:\n") + call pargstr (mtfile) + + totrecords = 0 + nrecords = 0 + totbytes = 0 + last_recsize = 0 + + # Describe record composition of file. + while (read (in, Memc[buf], bufsize) != EOF) { + recsize = fstati (in, F_SZBBLK) + if (nrecords == 0) { # first record + nrecords = 1 + last_recsize = recsize + } else if (recsize == last_recsize) { + nrecords = nrecords + 1 + } else { + call fprintf (out, "\t%d %d-byte records\n") + call pargi (nrecords) + call pargi (last_recsize) + nrecords = 1 + last_recsize = recsize + } + totrecords = totrecords + 1 + totbytes = totbytes + recsize + } + + if (nrecords > 0) { + call fprintf (out, "\t%d %d-byte records\n") + call pargi (nrecords) + call pargi (last_recsize) + } + + # Print total count of records, bytes. + call fprintf (out, "\tTotal %d records, %d bytes\n") + call pargi (totrecords) + call pargi (totbytes) + + call mfree (buf, TY_CHAR) + call close (in) + + return (totrecords) +end + + +# MTCOPY -- Copy a binary file from magtape or disk to magtape or disk, +# using all the default FIO and MTIO pararameters. If the output file is +# a magtape, all records (except possibly the last record in the file) will +# be the same size. If input tape records are not commensurate with the size +# of a CHAR they will be zero-padded to an integral number of chars upon +# input. + +procedure t_mtcopy() + +pointer buf +int in, out, bufsize, acmode +char infile[SZ_FNAME], outfile[SZ_FNAME] +int mtopen(), fstati(), read(), mtfile() + +begin + call clgstr ("infile", infile, SZ_FNAME) + call clgstr ("outfile", outfile, SZ_FNAME) + + in = mtopen (infile, READ_ONLY, 0) + + # If output file is a disk file, create a new file, but do not + # create a new tape if writing to tape. + + acmode = NEW_FILE + if (mtfile(outfile) == YES) + acmode = WRITE_ONLY + out = mtopen (outfile, acmode, 0) + + bufsize = fstati (in, F_BUFSIZE) + call malloc (buf, bufsize, TY_CHAR) + + while (read (in, Memc[buf], bufsize) != EOF) + call write (out, Memc[buf], fstati (in, F_NCHARS)) + + call mfree (buf, TY_CHAR) + call close (in) + call close (out) +end + + +# REWIND -- Rewind the tape. + +procedure t_rewind() + +char mtname[SZ_FNAME] +bool clgetb() +int btoi() + +begin + call clgstr ("mtname", mtname, SZ_FNAME) + call mtrewind (mtname, btoi(clgetb("initialize"))) +end |