2019-05-01 06:24:33 -07:00

1322 lines
30 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (C) 2018 John Forecast. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JOHN FORECAST "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Support routines for handling DOS/BATCH-11 magtapes under fsio
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <sys/stat.h>
#include "fsio.h"
/*
* Table of "set" commands
*/
static char *setCmds[] = {
"uic",
"prot",
NULL
};
#define DOSMTSET_UIC 0
#define DOSMTSET_PROT 1
extern int args;
extern char **words;
/*++
* d o s m t P a r s e F i l e s p e c
*
* Parse a character string representing a DOS-11 magtape file specification.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* ptr - pointer to the file specification string
* spec - pointer to the file specification block
* wildcard - wildcard processing options:
* 0 (DOSMT_M_NONE) - wildcards not allowed
* 1 (DOSMT_M_ALLOW) - wildcards allowed
* 2 (DOSMT_M_NONAME) - wildcards allowed
* if filename + ext not
* present default to *.*[*,*]
*
* Outputs:
*
* The file specification block will be filled with the file information.
*
* Returns:
*
* 1 if parse is successful, 0 otherwise
*
--*/
#define P_DONE 0
#define P_NAME 1
#define P_EXT 2
#define P_PROJ 3
#define P_PROG 4
int dosmtParseFilespec(
struct mountedFS *mount,
char *ptr,
struct dosmtFileSpec *spec,
int wildcard
)
{
struct DOSMTdata *data = &mount->dosmtdata;
char term[] = { '\0', '.', '[', ',', ']' };
char flags[] =
{ 0, DOSMT_WC_NAME, DOSMT_WC_EXT, DOSMT_WC_PROJ, DOSMT_WC_PROG };
int state = P_NAME;
unsigned int uic, i, namemax = ((mount->flags & FS_DOSMTEXT) != 0) ? 9 : 6;
char filename[9], ext[3];
memset(spec, 0, sizeof(struct dosmtFileSpec));
spec->proj = data->proj;
spec->prog = data->prog;
memset(&filename, ' ', sizeof(filename));
memset(&ext, ' ', sizeof(ext));
if (wildcard == DOSMT_M_NONAME)
spec->flags = DOSMT_WC_NAME | DOSMT_WC_EXT | DOSMT_WC_PROJ | DOSMT_WC_PROG;
while (state != P_DONE) {
if (wildcard) {
if (*ptr == '*') {
spec->flags |= flags[state];
ptr++;
if (*ptr == '\0')
state = P_DONE;
else if (*ptr == term[state]) {
if (state == P_PROG)
state = P_DONE;
else state++;
} else return 0;
ptr++;
continue;
}
}
i = 0;
switch (state) {
case P_NAME:
while ((*ptr != '\0') &&
(*ptr != '.') &&
(strchr(rad50, toupper(*ptr)) != NULL) &&
(i < namemax)) {
spec->flags &= ~DOSMT_WC_NAME;
filename[i++] = toupper(*ptr++);
}
switch (*ptr++) {
case '\0':
state = P_DONE;
break;
case '.':
state = P_EXT;
break;
case '[':
state = P_PROJ;
break;
default:
return 0;
}
break;
case P_EXT:
while ((*ptr != '\0') &&
(strchr(rad50, toupper(*ptr)) != NULL) &&
(i < sizeof(ext))) {
spec->flags &= ~DOSMT_WC_EXT;
ext[i++] = toupper(*ptr++);
}
switch (*ptr++) {
case '\0':
state = P_DONE;
break;
case '[':
state = P_PROJ;
break;
default:
return 0;
}
break;
case P_PROJ:
spec->flags &= ~(DOSMT_WC_PROJ | DOSMT_WC_PROG);
uic = 0;
while ((strchr("01234567", *ptr) != NULL) && (i < 3)) {
uic = (uic << 3) | (*ptr++ - '0');
i++;
}
if ((uic == 0) || (uic > 0377))
return 0;
spec->proj = uic & 0377;
if (*ptr++ != ',')
return 0;
state = P_PROG;
break;
case P_PROG:
uic = 0;
while ((strchr("01234567", *ptr) != NULL) && (i < 3)) {
uic = (uic << 3) | (*ptr++ - '0');
i++;
}
if ((uic == 0) || (uic > 0377))
return 0;
spec->prog = uic & 0377;
if (*ptr++ != ']')
return 0;
state = P_DONE;
break;
}
}
spec->name[0] = ascR50(&filename[0]);
spec->name[1] = ascR50(&filename[3]);
spec->name[2] = ascR50(&filename[6]);
spec->ext = ascR50(&ext[0]);
return 1;
}
/*++
* d o s m t L o o k u p F i l e
*
* Lookup up a specific file on the tape. If the "-n" switch is set, the
* tape will not be rewound before starting the lookup.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* spec - pointer to the file specification block
* file - pointer to open file descriptor to receive results
*
* Outputs:
*
* The mount point specific buffer area will be modified.
*
* Returns:
*
* 1 if file found, 0 otherwise
*
--*/
int dosmtLookupFile(
struct mountedFS *mount,
struct dosmtFileSpec *spec,
struct dosmtOpenFile *file
)
{
struct DOSMTdata *data = &mount->dosmtdata;
uint32_t status;
if (!SWISSET('n'))
tapeRewind(mount->container);
do {
switch (status = tapeReadRecord(mount->container, data->buf, DOSMTRCLNT)) {
case ST_FAIL:
goto error;
case ST_EOM:
break;
case ST_TM:
/* Second tape mark in a row - treat as end-of-media */
status = ST_EOM;
break;
default:
if ((status & ST_ERROR) == 0) {
if (status == sizeof(struct dosmthdr)) {
struct dosmthdr *hdr = (struct dosmthdr *)data->buf;
/*
* Determine if this file matches the filespec.
*/
if ((spec->flags & DOSMT_WC_PROJ) == 0)
if (spec->proj != hdr->proj)
goto nomatch;
if ((spec->flags & DOSMT_WC_PROG) == 0)
if (spec->prog != hdr->prog)
goto nomatch;
if ((spec->flags & DOSMT_WC_NAME) == 0) {
if ((spec->name[0] != le16toh(hdr->fname[0])) ||
(spec->name[1] != le16toh(hdr->fname[1])))
goto nomatch;
if ((mount->flags & FS_DOSMTEXT) != 0)
if (spec->name[2] != le16toh(hdr->fname3))
goto nomatch;
}
if ((spec->flags & DOSMT_WC_EXT) == 0)
if (spec->ext != le16toh(hdr->ext))
goto nomatch;
/*
* Found a matching file.
*/
file->mount = mount;
return 1;
} else goto error;
}
nomatch:
/*
* Skip to next file.
*/
do {
if ((status = tapeReadRecordLength(mount->container)) == ST_FAIL)
goto error;
} while ((status != ST_EOM) && (status != ST_TM));
if (status == ST_EOM)
tapeSetPosition(mount->container, data->eot);
}
} while (status != ST_EOM);
error:
/*
* Make sure we leave the tape at a valid position.
*/
tapeSetPosition(mount->container, data->eot);
return 0;
}
/*++
* d o s m t R e a d B y t e s
*
* Read a sequence of bytes from an open file.
*
* Inputs:
*
* file - pointer to an open file descriptor
* buf - pointer to a buffer to receive the data
* len - # of bytes of data to read
*
* Outputs:
*
* The buffer will be overwritten by up to "len" bytes
*
* Returns:
*
* # of bytes read from the file (may be less than "len"), 0 if EOF
*
--*/
static int dosmtReadBytes(
struct dosmtOpenFile *file,
char *buf,
int len
)
{
struct mountedFS *mount = file->mount;
int count = 0;
uint32_t status;
off_t pos;
while (len) {
if (file->nextb == DOSMTRCLNT) {
if (file->tm != 0)
return count;
pos = tapeGetPosition(mount->container);
switch (status = tapeReadRecord(mount->container, file->buf, DOSMTRCLNT)) {
case ST_FAIL:
return 0;
case ST_TM:
case ST_EOM:
file->tm = 1;
return count;
default:
if ((status & ST_ERROR) || (status != DOSMTRCLNT)) {
tapeSetPosition(mount->container, pos);
return count;
}
file->nextb = 0;
break;
}
}
*buf++ = file->buf[file->nextb++];
len--;
count++;
}
return count;
}
/*++
* d o s m t W r i t e B y t e s
*
* Write a sequence of bytes to an open DOS-11 magtape file.
*
* Inputs:
*
* file - pointer to an open file descriptor
* buf - pointer to a buffer to receive the data
* len - # of bytes of data to read
*
* Outputs:
*
* None
*
* Returns:
*
* # of bytes written to the file (may be less than "len"), 0 if error
*
--*/
static int dosmtWriteBytes(
struct dosmtOpenFile *file,
char *buf,
int len
)
{
struct mountedFS *mount = file->mount;
int count = 0;
if (file->error != 0)
return 0;
while (len) {
file->buf[file->nextb++] = *buf++;
len--;
count++;
if (file->nextb == DOSMTRCLNT) {
if (tapeWriteRecord(mount->container, file->buf, DOSMTRCLNT) == 0) {
file->error = 1;
return count;
}
memset(file->buf, 0, DOSMTRCLNT);
file->nextb = 0;
}
}
return count;
}
/*++
* d o s m t M o u n t
*
* Verify that the open container file is in valid .tap format and that it
* consists of a number of files each preceeded by a file header. Strict
* DOS-11 tapes will have a 12 byte file header but there are some tapes
* will use an extended, 14 byte header with an extra 3 characters in the
* filename.
*
* Inputs:
*
* mount - pointer to a mounted file system descriptor
* (not in the mounted file system list)
*
* Outputs:
*
* None
*
* Returns:
*
* 1 if a valid DOS-11 magtape, 0 otherwise
*
--*/
static int dosmtMount(
struct mountedFS *mount
)
{
struct DOSMTdata *data = &mount->dosmtdata;
if (tapeVerify(mount->container, &data->eot)) {
unsigned char buf[DOSMTRCLNT];
uint32_t status;
/*
* Scan each file making sure it is preceeded by a dos11 header (either
* version is valid).
*/
do {
/*
* If we are at end-of-tape, everything is OK
*/
if (tapeGetPosition(mount->container) == data->eot)
break;
switch (status = tapeReadRecord(mount->container, buf, sizeof(buf))) {
case ST_FAIL:
return 0;
case ST_EOM:
break;
case ST_TM:
/* Second tape mark in a row - treat as end of media */
status = ST_EOM;
break;
default:
/*
* If the header has an error, unable to use this tape.
*/
if ((status & ST_ERROR) != 0)
return 0;
status &= ST_LENGTH;
if (status != sizeof(struct dosmthdr))
return 0;
/*
* Scan forward to the end of this file.
*/
do {
switch (status = tapeReadRecordLength(mount->container)) {
case ST_FAIL:
return 0;
case ST_EOM:
case ST_TM:
break;
default:
if ((status & ST_ERROR) == 0)
if ((status & ST_LENGTH) != DOSMTRCLNT)
return 0;
}
} while ((status != ST_EOM) && (status != ST_TM));
}
} while (status != ST_EOM);
data->proj = 01;
data->prog = 01;
data->prot = 0233;
if (SWISSET('x'))
mount->flags |= FS_DOSMTEXT;
/*
* Position the tape at the beginning
*/
tapeRewind(mount->container);
return 1;
} else fprintf(stderr, "mount: Invalid tape format\n");
return 0;
}
/*++
* d o s m t U m o u n t
*
* Unmount the DOS-11 magtape file system, releasing any storage allocated.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void dosmtUmount(
struct mountedFS *UNUSED(mount)
)
{
}
/*++
* d o s m t S e t
*
* Set mount point/filesystem specific values.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
* unit - device unit number (unused)
* present - device unit number present (unused)
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void dosmtSet(
struct mountedFS *mount,
uint8_t UNUSED(unit),
uint8_t UNUSED(present)
)
{
struct DOSMTdata *data = &mount->dosmtdata;
int idx = 0;
uint8_t prog, proj, prot;
while (setCmds[idx] != NULL) {
if (strcmp(words[1], setCmds[idx]) == 0) {
switch (idx) {
case DOSMTSET_UIC:
if (args == 3) {
if (sscanf(words[2], "[%hho,%hho]", &proj, &prog) == 2) {
data->proj = proj;
data->prog = prog;
} else fprintf(stderr,
"dosmt: UIC syntax error \"%s\"\n", words[2]);
} else fprintf(stderr, "dosmt: Invalid syntax for \"set uic\"\n");
return;
case DOSMTSET_PROT:
if (args == 3) {
if (sscanf(words[2], "<%hho>", &prot) == 1) {
data->prot = prot;
} else fprintf(stderr,
"dosmt: Protection syntax error \"%s\"\n", words[2]);
} else fprintf(stderr, "dosmt: Invalid syntax for \"set prot\"\n");
return;
}
}
idx++;
}
fprintf(stderr, "dosmt: Unknown set command \"%s\"\n", words[1]);
}
/*++
* d o s m t I n f o
*
* Display information about the current position of the tape.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
* unit - device unit number (unused)
* present - device unit number present (unused)
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void dosmtInfo(
struct mountedFS *mount,
uint8_t UNUSED(unit),
uint8_t UNUSED(present)
)
{
struct DOSMTdata *data = &mount->dosmtdata;
off_t pos = tapeGetPosition(mount->container);
int eot = 0, bot = pos == 0;
char temp[32], filename[14];
struct dosmthdr *hdr = (struct dosmthdr *)data->buf;
struct stat stat;
fstat(fileno(mount->container), &stat);
/*
* If we are positioned at the end-of-file, there is a tape mark or
* end-of-media marker missing. Treat it as though the missing marker
* is present.
*/
if (pos != stat.st_size) {
uint32_t bc;
switch (bc = tapeReadRecord(mount->container, data->buf, DOSMTRCLNT)) {
case ST_FAIL:
goto error;
case ST_TM:
case ST_EOM:
eot = 1;
break;
default:
if ((bc & ST_ERROR) == 0) {
if (bc == sizeof(struct dosmthdr)) {
memset(filename, ' ', sizeof(filename));
filename[13]='\0';
r50Asc(le16toh(hdr->fname[0]), &filename[0]);
r50Asc(le16toh(hdr->fname[1]), &filename[3]);
if ((mount->flags & FS_DOSMTEXT) != 0)
r50Asc(le16toh(hdr->fname3), &filename[6]);
filename[9] = '.';
r50Asc(le16toh(hdr->ext), &filename[10]);
break;
}
}
strcpy(filename, "Unknown");
break;
}
/*
* Restore the tape position
*/
if (tapeSetPosition(mount->container, pos) != 0)
goto error;
} else eot = 1;
if ((bot == 0) && (eot == 0)) {
unsigned long long position = pos;
sprintf(temp, "Offset %llu", position);
}
printf("%s:\n", mount->name);
printf(" Position: %s\n",
bot ? "Beginning of tape" :
(eot ? "End of tape" : temp));
if (!eot)
printf(" Next file: %s[%3o,%3o]\n",
filename, hdr->proj, hdr->prog);
return;
error:
fprintf(stderr, "info: container file I/O error\n");
tapeRewind(mount->container);
}
/*++
* d o s m t D i r
*
* Produce a full or brief directory listing. After listing the tape will
* be positioned at beginning-of-tape. If the '-n' switch is specified,
* the directory will be listed from the current tape position.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
* unit - device unit number (unused)
* fname - pointer to filename string
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void dosmtDir(
struct mountedFS *mount,
uint8_t UNUSED(unit),
char *fname
)
{
struct DOSMTdata *data = &mount->dosmtdata;
struct dosmtFileSpec spec;
uint32_t status;
if (dosmtParseFilespec(mount, fname, &spec, DOSMT_M_NONAME) == 0) {
fprintf(stderr, "dir: syntax error in file spec \"%s\"\n", fname);
return;
}
/*
* Position to beginning-of-tape unless the '-n' switch is specified.
*/
if (!SWISSET('n'))
tapeRewind(mount->container);
do {
switch (status = tapeReadRecord(mount->container, data->buf, DOSMTRCLNT)) {
case ST_FAIL:
return;
case ST_EOM:
break;
case ST_TM:
/* Second tape mark in a row - treat as end-of-media */
status = ST_EOM;
break;
default:
if ((status & ST_ERROR) == 0) {
if (status == sizeof(struct dosmthdr)) {
char filename[14], sdate[12];
struct dosmthdr *hdr = (struct dosmthdr *)data->buf;
uint16_t blocks = 0;
uint32_t length;
/*
* Compute the number of blocks in this file.
*/
do {
switch (length = tapeReadRecordLength(mount->container)) {
case ST_FAIL:
return;
case ST_EOM:
case ST_TM:
break;
default:
if ((length & ST_ERROR) == 0)
blocks++;
break;
}
} while ((length != ST_EOM) && (length != ST_TM));
/*
* Determine if this file matches the filespec.
*/
if ((spec.flags & DOSMT_WC_PROJ) == 0)
if (spec.proj != hdr->proj)
continue;
if ((spec.flags & DOSMT_WC_PROG) == 0)
if (spec.prog != hdr->prog)
continue;
if ((spec.flags & DOSMT_WC_NAME) == 0) {
if ((spec.name[0] != le16toh(hdr->fname[0])) ||
(spec.name[1] != le16toh(hdr->fname[1])))
continue;
if ((mount->flags & FS_DOSMTEXT) != 0)
if (spec.name[2] != le16toh(hdr->fname3))
continue;
}
if ((spec.flags & DOSMT_WC_EXT) == 0)
if (spec.ext != le16toh(hdr->ext))
continue;
memset(filename, ' ', sizeof(filename));
filename[13] = '\0';
r50Asc(le16toh(hdr->fname[0]), &filename[0]);
r50Asc(le16toh(hdr->fname[1]), &filename[3]);
if ((mount->flags & FS_DOSMTEXT) != 0)
r50Asc(le16toh(hdr->fname3), &filename[6]);
filename[9] = '.';
r50Asc(le16toh(hdr->ext), &filename[10]);
if (SWISSET('f')) {
dos11Date(le16toh(hdr->date), sdate);
printf("%s %5d. %s <%03o> [%3o,%3o]\n",
filename, blocks, sdate,
le16toh(hdr->prot), hdr->proj, hdr->prog);
} else printf("%s [%3o,%3o]\n", filename, hdr->proj, hdr->prog);
} else fprintf(stderr, " *** Unexpected record size\n");
} else fprintf(stderr, " *** Directory entry contains error\n");
}
} while (status != ST_EOM);
tapeRewind(mount->container);
}
/*++
* d o s m t O p e n F i l e R
*
* Open a DOS-11 magtape file for reading.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
* unit - device unit number (unused)
* fname - pointer to filename string
*
* Outputs:
*
* None
*
* Returns:
*
* Pointer to open file descriptor, NULL if open fails
*
--*/
static void *dosmtOpenFileR(
struct mountedFS *mount,
uint8_t UNUSED(unit),
char *fname
)
{
struct dosmtOpenFile *file;
struct dosmtFileSpec spec;
if (dosmtParseFilespec(mount, fname, &spec, DOSMT_M_NONAME) == 0) {
fprintf(stderr, "Failed to parse filename \"%s\"\n", fname);
return NULL;
}
if ((file = malloc(sizeof(struct dosmtOpenFile))) != NULL) {
if (dosmtLookupFile(mount, &spec, file) != 0) {
file->mode = M_RD;
file->nextb = DOSMTRCLNT;
file->tm = 0;
file->error = 0;
} else {
free(file);
return NULL;
}
}
return file;
}
/*++
* d o s m t O p e n F i l e W
*
* Open a DOS-11 magtape file for writing.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
* unit - device unit number (unused)
* fname - pointer to filename string
* size - estimated file size (in bytes)
*
* Outputs:
*
* None
*
* Returns:
*
* Pointer to open file descriptor, NULL if open fails
*
--*/
static void *dosmtOpenFileW(
struct mountedFS *mount,
uint8_t UNUSED(unit),
char *fname,
off_t UNUSED(size)
)
{
struct dosmtOpenFile *file;
struct dosmtFileSpec spec;
struct dosmthdr hdr;
struct tm tm;
time_t now = time(NULL);
uint16_t today;
if (dosmtParseFilespec(mount, fname, &spec, DOSMT_M_NONE) == 0) {
fprintf(stderr, "Failed to parse filename \"%s\"\n", fname);
return NULL;
}
if ((file = malloc(sizeof(struct dosmtOpenFile))) != NULL) {
/*
* Compute a suitable year for file creation date. This year will have
* the same calendar as the current year but will be in the 20th century
* so that DOS/BATCH-11 will be able to interpret it correctly.
*/
localtime_r(&now, &tm);
tm.tm_year -= 28;
today = ((tm.tm_year - 70) * 1000) + tm.tm_yday + 1;
hdr.fname[0] = htole16(spec.name[0]);
hdr.fname[1] = htole16(spec.name[1]);
if ((mount->flags & FS_DOSMTEXT) != 0)
hdr.fname3 = htole16(spec.name[2]);
else hdr.fname3 = 0;
hdr.ext = htole16(spec.ext);
hdr.prog = spec.prog;
hdr.proj = spec.proj;
hdr.prot = htole16(mount->dosmtdata.prot);
hdr.date = htole16(today);
if (tapeWriteRecord(mount->container, &hdr, sizeof(hdr)) == 0) {
free(file);
return NULL;
}
file->mode = M_WR;
file->mount = mount;
file->nextb = 0;
file->tm = 0;
file->error = 0;
}
return file;
}
/*++
* d o s m t F i l e S i z e
*
* Return an estimate of the size of an open DOS-11 magtape file.
*
* Inputs:
*
* filep - pointer to open file descriptor
*
* Outputs:
*
* None
*
* Returns:
*
* Estimated size of the file
*
--*/
static off_t dosmtFileSize(
void *filep
)
{
struct dosmtOpenFile *file = filep;
struct mountedFS *mount = file->mount;
off_t pos = tapeGetPosition(mount->container);
off_t size = 0;
uint32_t length;
/*
* Compute the length of this file. The estimate may larger than the
* actual size of the file size don't know the actual EOF position
* within the last block of the file.
*/
do {
switch (length = tapeReadRecordLength(mount->container)) {
case ST_FAIL:
size = 0;
length = ST_EOM;
/* FALLTHROUGH */
case ST_EOM:
case ST_TM:
break;
default:
size += DOSMTRCLNT;
break;
}
} while((length != ST_EOM) && (length != ST_TM));
tapeSetPosition(mount->container, pos);
return size;
}
/*++
* d o s m t C l o s e F i l e
*
* Close an open DOS-11 magtape file.
*
* Inputs:
*
* filep - pointer to open file descriptor
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void dosmtCloseFile(
void *filep
)
{
struct dosmtOpenFile *file = filep;
struct mountedFS *mount = file->mount;
struct DOSMTdata *data = &mount->dosmtdata;
uint32_t status;
if (file->mode == M_RD) {
if (file->tm == 0) {
/*
* Tape mark not seen, skip to end of current file.
*/
do {
if ((status = tapeReadRecordLength(mount->container)) == ST_FAIL) {
fprintf(stderr,
"Error positioning \"%s\", rewinding\n", mount->name);
tapeRewind(mount->container);
status = ST_TM;
}
} while ((status != ST_EOM) && (status != ST_TM));
if (status == ST_EOM)
tapeSkipRecordR(mount->container);
}
} else {
if (file->nextb != 0) {
/*
* The are some pending output byte(s), flush a final record to the tape.
*/
if (tapeWriteRecord(mount->container, file->buf, DOSMTRCLNT) == 0)
file->error = 1;
}
if (tapeWriteTM(mount->container) == 0)
file->error = 1;
data->eot = tapeGetPosition(mount->container);
if (tapeWriteEOM(mount->container, 1) == 0)
file->error = 1;
if (file->error != 0) {
ERROR("Panic: Error writing on \"%s\"\n", mount->name);
exit(3);
}
}
free(file);
}
/*++
* d o s m t R e a d F i l e
*
* Read data from a DOS-11 magtape to a supplied buffer.
*
* Inputs:
*
* filep - pointer to open file descriptor
* buf - pointer to buffer
* buflen - length of the supplied buffer
*
* Outputs:
*
* None
*
* Returns:
*
* # of bytes of data read, 0 means EOF or error
*
--*/
static size_t dosmtReadFile(
void *filep,
void *buf,
size_t buflen
)
{
struct dosmtOpenFile *file = filep;
char *bufr = buf;
return dosmtReadBytes(file, bufr, buflen);
}
/*++
* d o s m t W r i t e F i l e
*
* Write data to a DOS-11 magtape file from a supplied buffer.
*
* Inputs:
*
* filep - pointer to open file descriptor
* buf - pointer to buffer
* buflen - length of the supplied buffer
*
* Outputs:
*
* None
*
* Returns:
*
* # of bytes of data written, 0 means error
*
--*/
size_t dosmtWriteFile(
void *filep,
void *buf,
size_t buflen
)
{
struct dosmtOpenFile *file = filep;
char *bufw = buf;
return dosmtWriteBytes(file, bufw, buflen);
}
/*++
* d o s m t R e w i n d
*
* Rewind the tape.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void dosmtRewind(
struct mountedFS *mount
)
{
tapeRewind(mount->container);
}
/*++
* d o s m t E O M
*
* Position the tape just before the final tape mark or end of media marker.
* A subsequent write operation will append to the tape. Note that we have
* special case an empty container file containg <TM><TM> or <TM><EOM>
* where we need to position to the beginning of the container rather than
* after the first <TM>.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void dosmtEOM(
struct mountedFS *mount
)
{
struct DOSMTdata *data = &mount->dosmtdata;
if (tapeEOM(mount->container, &data->eot) == 0)
fprintf(stderr, "eom: Failed to position device\n");
}
/*++
* d o s m t S k i p F
*
* Skip forward over a specified number of files. If end-of-media is reached,
* the skip operation will terminate early.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
* count - # of files to skip
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void dosmtSkipF(
struct mountedFS *mount,
unsigned long count
)
{
if (tapeSkipForward(mount->container, count) == 0)
fprintf(stderr, "skipf: Failed to position device\n");
}
/*++
* d o s m t S k i p R
*
* Skip backwards over a specified number of files. If end-of-media is
* reached, the skip operation will terminate early.
*
* Inputs:
*
* mount - pointer to the mounted file system descriptor
* count - # of files to skip
*
* Outputs:
*
* None
*
* Returns:
*
* None
*
--*/
static void dosmtSkipR(
struct mountedFS *mount,
unsigned long count
)
{
if (tapeSkipReverse(mount->container, count) == 0)
fprintf(stderr, "skipf: Failed to position device\n");
}
/*++
* d o s m t F S
*
* Descriptor for access DOS/BATCH-11 magtapes.
*
--*/
struct FSdef dosmtFS = {
NULL,
"dosmt",
"dosmt PDP-11 DOS/BATCH-11 magtape access\n",
FS_TAPE | FS_EMPTYFILE | FS_1OPENFILE,
0,
dosmtMount,
dosmtUmount,
NULL,
NULL,
dosmtSet,
dosmtInfo,
dosmtDir,
dosmtOpenFileR,
dosmtOpenFileW,
dosmtFileSize,
dosmtCloseFile,
dosmtReadFile,
dosmtWriteFile,
NULL,
dosmtRewind,
dosmtEOM,
dosmtSkipF,
dosmtSkipR
};