/* @(#)osu.c 17.1.1.1 (ESO-IPG) 01/25/02 17:35:09 */ /*=========================================================================== Copyright (C) 1995 European Southern Observatory (ESO) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, MA 02139, USA. Corresponding concerning ESO-MIDAS should be addressed as follows: Internet e-mail: midas@eso.org Postal address: European Southern Observatory Data Management Division Karl-Schwarzschild-Strasse 2 D 85748 Garching bei Muenchen GERMANY ===========================================================================*/ /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .TYPE Module .NAME osu .LANGUAGE C .AUTHOR Francois Ochsenbein [ESO] .CATEGORY Interface to i/o on devices like tapes / OD / etc .ENVIRONMENT VAX / VMS or Unix .COMMENTS The functions of this module perform basic i/o to/from magnetic tape and optical disks on VAX/VMS environment. The maximum number of available special units is assumed to be MAXDEV (defined here) \begin{TeX} \\ {\bf Some Troubleshooting}: With TD interface, the error ``Invalid Buffer Length" arises when the read/write buffer is not aligned at a 4-byte address... {\bf Restrictions}: read/write on (optical) disks is limited to multiple of sectors. A file, called DEVCAPFILE, is used to describe the various parameters of the device(s). Each device is described in this file, as {\tt node\_name{\bf!}device\_name} where {\tt device\_name} is the {\em physical} name of the device. Properties are described as {\tt pp}$=${value}, where {\tt pp} is a 2-letter code specifying the item. The most important items in this file are: \begin{itemize} \item {\tt bs}$=$ blocksize (in bytes) \item {\tt ss}$=$ sector size (in bytes) \item {\tt us}$=$ Unit size in sectors \item {\tt ds}$=$ density (concerns tapes only) \item {\tt dc}$=$ Reference to another device in the DEVCAPFILE \item {\tt cl}$=$ Device Class Name. This Name specifies the iodev class which must be used to access the lower-level io functions. \item {\tt bw}$=$ 0 if Backwards Movements are NOT available. \end{itemize} \end{TeX} .VERSION 1.0 10-Mar-1986: Creation .VERSION 1.1 21-Mar-1986 : Modified io1 and io2 for direct-access devices. .VERSION 1.2 17-Dec-1986: New FCB definition .VERSION 1.3 21-May-1987: Suppress warning message .VERSION 2.0 24-Feb-1988: Integrated as osu routines. Tested on Gigadisk, Maxtor .VERSION 3.0 03-Agu-1988: Implementation in mVAX-Ultrix. C Guirao. .VERSION 3.1 28-Nov-1988: Implementation in VAX/VMS. C Guirao. .VERSION 3.2 28-Nov-1988: Using MAXIO1 record. C Guirao. .VERSION 3.3 05-May-1989: Forcing "tmove()" reading to check double file mark. Translating WRITE in READ_WRITE in open mode, and checking the proper mode in osuread. C Guirao. .VERSION 3.4 18-Jul-1989: Devices are described in a file named $DEVCAPFILE Added osubsize to get blocksize .VERSION 4.0 891012: Use of external functions. .VERSION 4.1 891027: Be sure that DEVCAPFILE exists. Added tm (number of tm for End of Tape) .VERSION 4.2 891220: Removed bug in scandev (F.O.) Added functions for Disks, and EOM (End-Of-Media). .VERSION 4.3 900111: Added osustat. Added NULL devices. .VERSION 4.4 900207: Added iodevr for remote tapes. CG .VERSION 4.5 900412: BUFTEST size increased 30K for exabyte tapes. CG .VERSION 4.6 900518: Added a check on recursive loop on findclass. FO osuclose has now 2 parameters (DISMOUNT option) .VERSION 4.7 901114: no limit for usize .VERSION 4.8 901120: osuclose properly handled for direct-access dev. .VERSION 5.0 910919: When an error writing, the keep osermsg and oserror .VERSION 6.0 920217: Separator between hostname & devicename also ':' .VERSION 7.0 920625: Allowing multi-skip operations tmove() & bmove().CG .VERSION 7.1 920821: osuopen and block_no == 0, we are after a FM. CG. -----------------------------------------------------------------------------*/ #ifdef NO_TAPE_REMOTE /* IODEV0: First "iodevg" in the list */ #define IODEV0 iodevg #else #define IODEV0 iodevr #endif #define MAXDEV 4 /* Maximal number of opened devices */ #include /* ANSI-C prototyping */ #include /* Keywords definitions */ #include /* Internal to osu / iodev */ #include /* Contains NULL_DEV definition */ #include /* Contains devstat structure */ #include /* Contains READ, WRITE, etc... */ #include /* Function definitions */ #include /* System error definitions */ #include /* Midas error definitions */ #include typedef int (*FCT)(); #define MAX(x,y) ((x) >= (y) ? (x):(y) ) /* Maximum */ #define MIN(x,y) ((x) <= (y) ? (x):(y) ) /* Minimum */ #define NULL_PTR(x) (x *)0 #define BUFTEST 30*1024 /* The size of a test buffer */ #define DEFAULT_DENSITY 1600 /* Density by default */ #define DEFAULT_SECTORSIZE 512 /* Size of one disk sector */ #define START_DEV 100 /* First Number */ /* Definition of Functions */ #define ioinfo(f,b,fn,bn) (*fcb->ops[U_INFO])(f,b,fn,bn) #define ioopen(f,m,d) (*fcb->ops[U_OPEN])(f,m,d) #define ioclose(f,o) (*fcb->ops[U_CLOSE])(f,o) #define ioread(f,b,l) (*fcb->ops[U_READ])(f,b,l) #define iowrite(f,b,l) (*fcb->ops[U_WRITE])(f,b,l) #define iorew(f) (*fcb->ops[U_REWIND])(f) #define ioeom(f) (*fcb->ops[U_EOM])(f) #define ioweof(f) (*fcb->ops[U_WEOF])(f,1) #define iofsf(f,n) (*fcb->ops[U_FMF])(f,n) #define iobsf(f,n) (*fcb->ops[U_FMB])(f,n) #define iofsr(f,n) (*fcb->ops[U_BMF])(f,n) #define iobsr(f,n) (*fcb->ops[U_BMB])(f,n) #define iosread(f,s,ss,b,l) (*fcb->ops[U_SREAD])(f,s,ss,b,l) #define ioswrite(f,s,ss,b,l) (*fcb->ops[U_SWRITE])(f,s,ss,b,l) /* This structure, created for each device, allows to know the current position on any device */ typedef int (*FCT_PTR)(); /* Pointer to function */ typedef struct { /* File Control Block */ char *name; /* PHYSICAL Device Name */ char *klass; /* Device Class Name */ unsigned char access; /* Access mode 0/1/2/3 */ #define FCB_AM 16 /* Append Mode available */ #define FCB_BW 32 /* Backwards available */ #define FCB_DA 64 /* Direct Access (Seek) */ #define FCB_TM 128 /* Tape Mode (erase's end) */ unsigned char status; /* status flags */ #define FCB_EOM 16 /* Tape in EOM */ #define FCB_NOCOUNT 32 /* Unknown block number */ /*#define FCB_NOFEET 64*/ /* Unknown feet count */ unsigned char last_op; /* Last operation performed */ int eotm; /* Number of TM to write at end */ /* long feet;*/ /* used tape length in feet/1000*/ long usize; /* bytes of file / total blocks */ long block_no; /* block position within file */ int tape_marks; /* Number of CONSECUTIVE TM */ int block_max; /* Maximum blocksize (tape) */ int block_min; /* Minimum blocksize (tape) */ int blocksize; /* Size of one block */ int sectorsize; /* Size of one sector */ int density; /* Density (Tapes), sector size */ int file_no; /* number of file (for MT) */ FCT_PTR ops[U_MAX+1]; /* Available Operations */ /* --- System-dependant Part----*/ int pfd; /* Tape file descriptor in UNIX */ } FCB; #define tgap (fcb->density == 6250? (1500/12) : (3000/12)) \ /* Gap on tape due to the Tape-Mark */ #define BADCLASSmsg "Class of Device unknown:" static FCB *Units[MAXDEV] = {0,0,0,0}; static FCB *fcb; static int ncb = START_DEV; /* Number of opened units */ static char hasbw; /* 1 if Backward available */ static char hasam; /* 1 if Append Mode available */ static struct { char msg[sizeof(BADCLASSmsg)]; char klass[20]; } theclass = { BADCLASSmsg, "generic" }; static struct osustat stbuf; static int findclass_loop = 0; static char *test_buffer; #define MAX_findclass 50 /* Recursivity limit to find a device class */ long osufseek(); /*===================================================================== Internal Routines *=====================================================================*/ static int osuerror(op) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Set Error Message .RETURNS -1 ----------------------------------------------------------------------*/ int op; /* IN: The operation to execute */ { static char msg[60] = "Function not available: "; register char *pm, *pl; static char *text[] = { /* U_INFO */ "info", /* U_OPEN */ "open", /* U_CLOSE */ "close", /* U_READ */ "read", /* U_WRITE */ "write", /* U_REWIND*/ "rewind", /* U_SREAD */ "sector_read", /* U_SWRITE*/ "sector_write", /* U_WEOF */ "write_EOF", /* U_FMF */ "file_move_forward", /* U_FMB */ "file_move_backwards", /* U_BMF */ "block_move_forward", /* U_BMB */ "block_move_backwards", /* U_EOM */ "to_EOMedia" }; oserror = -1; oserrmsg = msg; pm = &msg[24]; /* Append the signification of the function */ for(pl = text[op]; *pl; pl++) *(pm++) = *pl; *pm = '\0'; return(-1); } /* Declare here the various error functions */ static int err0() { return(osuerror(0));} static int err1() { return(osuerror(1));} static int err2() { return(osuerror(2));} static int err3() { return(osuerror(3));} static int err4() { return(osuerror(4));} static int err5() { return(osuerror(5));} static int err6() { return(osuerror(6));} static int err7() { return(osuerror(7));} static int err8() { return(osuerror(8));} static int err9() { return(osuerror(9));} static int err10() { return(osuerror(10));} static int err11() { return(osuerror(11));} static int err12() { return(osuerror(12));} static int err13() { return(osuerror(13));} static FCT err_fct[1+U_MAX] = { err0, err1, err2, err3, err4, err5, err6, err7, err8, err9, err10, err11, err12, err13}; static int uget(f) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Retrieve the FCB structure .RETURNS 0 (success) / -1 (failure) .REMARKS ----------------------------------------------------------------------*/ int f; /* IN: Unit number */ { oserrmsg = NULL_PTR(char); oserror = 0; fcb = NULL_PTR(FCB); if ((f >= START_DEV) && (f <= ncb)) fcb = Units[f-START_DEV]; if (fcb == NULL_PTR(FCB)) { oserror = -1; oserrmsg = "Bad Unit Number"; } return(oserror); } static int newfile() /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Update fcb information when a new file starts .RETURNS 0 .REMARKS For disks, should be called only at open... ----------------------------------------------------------------------*/ { fcb->status &= ~(FCB_NOCOUNT); /* count number is correct */ if (fcb->file_no < 0) { /* Beginning of Tape */ fcb->file_no = 0; /* First File */ } /* Increment the file counter */ else fcb->file_no += fcb->tape_marks; fcb->tape_marks= 0; /* No tape-mark encountered */ fcb->block_no = 0; /* block position within file */ fcb->block_max = 0; /* Maximum blocksize */ fcb->block_min = 0; /* Minimum blocksize */ fcb->status &= ~FCB_EOM; return(0); } static int newpos(bytes) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Computes the new position on the device .RETURNS 0 .REMARKS ----------------------------------------------------------------------*/ int bytes; /* IN: number of bytes read/ written */ { if (fcb->tape_marks) newfile(); if (fcb->access & FCB_DA) /* Direct Access */ { fcb->block_no += (bytes/fcb->sectorsize); if (bytes%fcb->sectorsize) fcb->block_no += 1; } else if (fcb->access & FCB_TM) (fcb->block_no)++; fcb->block_max = MAX(fcb->block_max, bytes); fcb->block_min = MIN(fcb->block_min, bytes); if (fcb->block_min <= 0) fcb->block_min = bytes; fcb->status &= ~FCB_EOM; return(0); } static int rewind_dev () /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Rewind the tape .RETURNS 0 (success) / -1 (failure) .REMARKS fcb updated. ----------------------------------------------------------------------*/ { iorew(fcb->pfd); if (oserror) return(-1); fcb->last_op = U_REWIND; fcb->file_no = -1; newfile(); return(0); } static int tfclose() /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Write EOF (Single TM) .RETURNS 0 (success) / -1 (failure) .REMARKS ----------------------------------------------------------------------*/ { ioweof(fcb->pfd); if (oserror == 0) { (fcb->tape_marks)++; fcb->last_op = U_WEOF; fcb->status |= FCB_EOM; } return(oserror ? -1 : 0); } static int bmove(num) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Move Backwards num TM. .RETURNS 0 (success) / -1 (failure) .REMARKS After move, the tape is positionned just before a tape-mark. ----------------------------------------------------------------------*/ int num; /* IN: Number of TM to move backward */ { if (fcb->tape_marks < num ) { fcb->file_no -= (num - fcb->tape_marks); fcb->tape_marks = 0; fcb->status |= (FCB_NOCOUNT); } else fcb->tape_marks -= num; if (fcb->file_no < 0) rewind_dev(); else iobsf(fcb->pfd,num), fcb->last_op = U_FMB; fcb->status &= ~FCB_EOM; return(oserror ? -1 : 0); } static int tmove(num) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Move Forwards num TM. .RETURNS 0 (success) / -1 (failure) .REMARKS After move, the tape is positionned after a tape-mark. ----------------------------------------------------------------------*/ int num; /* IN: Number of TM to move forward */ { register int l, lbuf; int w_error; char *w_msg; oserror = 0; if (num <= 0 || fcb->status & FCB_EOM) return(0); /* If a TM was previously detected, ensure a physical */ /* reading to force the move */ if (fcb->tape_marks) { lbuf = BUFTEST - (BUFTEST % fcb->blocksize); l = ioread(fcb->pfd, test_buffer, lbuf); oserror = 0; fcb->last_op = U_FMF; if (l == -1 || l == 0) { /* Generally End Of Media */ if (fcb->eotm > 1) { (fcb->tape_marks)++; bmove(1); } fcb->status |= FCB_EOM; return(0); } newfile(); /* Tape_marks reset to zero */ } if (iofsf(fcb->pfd, num) == -1) { w_error= oserror; w_msg = oserrmsg; rewind_dev(); oserror = w_error; oserrmsg = w_msg; return(-1); } fcb->file_no += (num - 1); fcb->last_op = U_FMF; fcb->status |= (FCB_NOCOUNT); fcb->tape_marks = 1; return(0); } static struct iolist *findclass(aclass) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Retrieve the class name .RETURNS Pointer to found class / NULL .REMARKS ----------------------------------------------------------------------*/ char *aclass; /* IN: Class to look for */ { struct iolist *plist, *IODEV0(); struct iolist *(*def)(); char *p, *q; /* Follow the linked list of iolist's */ if (++findclass_loop > MAX_findclass) return (NULL_PTR(struct iolist)); plist = (*IODEV0)(); for (def = IODEV0; def; def = plist->next) { plist = (*def)(); for(q=aclass, p=plist->klass; (*p == *q) && (*p); p++, q++) ; if ((*q == '\0') && (*p == '\0')) break; } if (!def) /* Class Not Found */ plist = NULL_PTR(struct iolist); return(plist); } static int getclass() /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Retrieve the class name, to retrieve operators. .RETURNS 0 / -1 (class doesn't exist) .REMARKS Functions added in FCB ----------------------------------------------------------------------*/ { struct iolist *plist; int i; OPITEM *pop; findclass_loop = 0; plist = findclass(theclass.klass); if (!plist) { oserror = -1, oserrmsg = (char *)&theclass; theclass.msg[sizeof(theclass.msg)-1] = ' '; return(-1); } fcb->klass = plist->klass; /* ** We have just to insert the functions in the list */ for (pop = plist->oplist, i = plist->nop; --i >= 0; pop++) fcb->ops[pop->opid] = pop->opf; return(0); } static int scandev(fd, dev) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Get Capabilities in opened DEVCAPFILE .RETURNS 0 / -1 .REMARKS Recursive call if `dc=' is found. ----------------------------------------------------------------------*/ int fd; /* IN: DEVCAPFILE file descriptor */ char *dev; /* IN: Device name */ { int i; char buf[133]; /* One line of DEVCAPFILE */ char *p, *q; /* Be sure dev is terminated with Null */ for (p = dev; isgraph(*p); p++) ; *p = '\0'; osaseek(fd, 0L, FILE_START); while (osaread(fd, buf, sizeof(buf)) >= 0) { if (buf[0] == '#') continue; for (p = buf, q = dev; *q && (*p == *q); p++, q++) ; if (*q == '\0' && (*p == ' ' || *p == '\t')) goto found_dev; } return(-1); found_dev: /* Here when device is found. Get interesting items, i.e. * bs= blocksize * us= unit size * ss= sector size * ds= density * cl= class_name * dc= Synonym */ while(*p) { while (isspace(*p)) p++; /* Skip blanks */ if(!*p) continue; /* Empty line */ if (*p == '\\') /* There is a continuation. Read Next record */ { next_record: if (osaread(fd, buf, sizeof(buf)) < 0) buf[0] = '\0'; if (buf[0] == '#') /* It's a comment */ goto next_record; p = buf; if (isspace(buf[0])) continue; goto terminated; } if (oscomp(p, "dc=", 3) == 0) /* Fetch another name */ return(scandev(fd, p+3)); if (oscomp(p, "bs=", 3) == 0) /* Blocksize */ { if (!fcb->blocksize) fcb->blocksize = atoi(p+3); } else if (oscomp(p, "us=", 3) == 0) /* Unit Size */ { if (!fcb->usize) fcb->usize = atoi(p+3); } else if (oscomp(p, "ss=", 3) == 0) /* Sector Size */ { if (!fcb->sectorsize) fcb->sectorsize = atoi(p+3); } else if (oscomp(p, "ds=", 3) == 0) /* Density */ { if (!fcb->density) fcb->density = atoi(p+3); } else if (oscomp(p, "tm=", 3) == 0) /* Number of tm */ { if (!fcb->eotm) fcb->eotm = atoi(p+3); } else if (oscomp(p, "am=", 3) == 0) /* BackWard */ { if (hasam == 2) hasam = atoi(p+3); } else if (oscomp(p, "bw=", 3) == 0) /* BackWard */ { if (hasbw == 2) hasbw = atoi(p+3); } else if (oscomp(p, "cl=", 3) == 0) /* Class */ { for (i=0, p+=3; (inext) { if (!class_name) /* Class not specified: look to next one */ plist = (*def)(); else plist = findclass(class_name); if (!plist) return(-1); /* Retrieve the item in list */ for (pop = plist->oplist, i = plist->nop; (--i >= 0) && (pop->opid != item->opid); pop++) ; if (i >= 0) /* I found the relevant item... */ { item->opf = pop->opf; return(0); } if (class_name) break; } return(-1); } char *osuname(f) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Retrieve name of an opened unit (current when f = -1) .RETURNS Name / NULL pointer if failed ----------------------------------------------------------------------*/ int f; /* IN: The unit number */ { if (f != -1) if (uget(f)) return((char *)0); /* Bad unit number... */ return(fcb ? fcb->name : NULL_PTR(char)); } int osumode(f) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Retrieve opening mode .RETURNS Mode / -1 when error ----------------------------------------------------------------------*/ int f; /* IN: The unit number */ { if (f != -1) if (uget(f)) return(-1); /* Bad unit number... */ return(fcb ? fcb->access & 3 : -1); } int osubsize(f) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Get the block size of the unit. .RETURNS The size of blocks for block devices (1 for tapes) ----------------------------------------------------------------------*/ int f; /* IN: The unit number */ { if (f != -1) if (uget(f)) return(-1); /* Bad unit number... */ return(fcb ? fcb->blocksize : -1); } int osustat(f, stat) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Get the status of the device .RETURNS 0 / -1 ----------------------------------------------------------------------*/ int f; /* IN: The unit number */ struct devstat *stat; /* OUT: Filled with known info */ { if (uget(f)) return(-1); /* Bad unit number... */ stat->name = fcb->name; stat->klass = fcb->klass; stat->density = fcb->density; stat->sectorsize = fcb->sectorsize; stat->usize = fcb->usize; stat->blocksize = fcb->blocksize; stat->file_no = fcb->file_no; stat->block_no = fcb->block_no; stat->block_max = fcb->block_max; stat->block_min = fcb->block_min; stat->tape_marks = fcb->tape_marks; return(0); } int osuopen (device, mode, den) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Assign (mount) a tape with a specified density .RETURNS The device number / -1 on error .REMARKS The default device class is "generic". The mount is performed here. If density is not specified or is zero, use DEFAULT_DENSITY for write-only; get the density otherwise. .REMARKS Open the device in APPEND mode does not work properly (it is like READ mode instead). It has been implemented changing the open mode to READ_WRITE and positioning the tape between the last two file marks. ----------------------------------------------------------------------*/ char *device; /* IN: The unit name, blank or NULL for null device */ int mode; /* IN: open mode: READ, WRITE, READ_WRITE, APPEND */ int den; /* IN: The density or sector size; 0 if default */ { register int l, i, no_host; register char *p, *q; char *devname, isnulldev; static char devhost[64], localhost[64]; static char host_dev[128]; /* Convert NULL device name if necessary */ devname = device; if (!devname) devname = ""; isnulldev = !*devname; /* Is zero for NULL device */ if (isnulldev) devname = NULL_DEV; oserror = 0; oserrmsg = NULL_PTR(char); fcb = NULL_PTR(FCB); hasam = 2; /* Unknown Parameter */ hasbw = 2; /* Unknown Parameter */ /* Check if there are not too many devices */ for (i = 0; i < MAXDEV; i++) if (!Units[i]) break; if (i >= MAXDEV) { oserror = -1; oserrmsg = "Too many opened osu Units" ; return(-1); } /* Allocate FCB, and Try to guess the device class from name: if device name contains a D, assume "disk" class */ l = strlen(devname); fcb = (FCB *)osmmget(sizeof(FCB) + l + 2); /* Note: 2 for EOS and ':' */ if (!fcb) return(-1); if (isnulldev) p = "dumb"; else { /* Look for a `d' for Disk, eitherwise Tape */ q = devname + oscbloc(devname, l, '/') + 1; l -= (q - devname); /* Keep filename */ if ((oscbloc(q, l, 'd') >= 0 ) || (oscbloc(q, l, 'D') >= 0)) p = "disk"; else p = "generic"; } oscopy(theclass.klass, p, 1+strlen(p)); /* Initialize the structure, and copy device name */ fcb->name = (char *)(fcb+1); fcb->access = mode; fcb->status = 0; fcb->eotm = 0; fcb->usize = 0; fcb->tape_marks = 0; fcb->block_max = 0; /* Maximum blocksize */ fcb->block_min = 0; /* Minimum blocksize */ fcb->blocksize = 0; fcb->sectorsize = 0; fcb->density = den; fcb->file_no = -1; for (l = 0; l < sizeof(fcb->ops)/sizeof(fcb->ops[0]); l++) fcb->ops[l] = err_fct[l]; no_host=0; for (q = devhost, p = devname; (*p) && (*p != '!') && (*p != ':'); p++, q++) *q= *p; if (*p == '!' || *p == ':' ) p++; else { p = devname; no_host=1; } for (q = fcb->name; *p; p++, q++) *q = *p; *q = '\0'; gethostname(localhost,64); /* * Fill fcb with Info from DEVCAPFILE * Copy device name to fcp->name , but strip hostname info. * If hostname not indicated and not found, then add localhost and * try again. */ if (no_host) { if ( getdev(devname) < 0 ) { for (q = host_dev, p = localhost; (*p); p++, q++) *q= *p; *q = ':'; for (q++, p = devname; (*p); p++, q++) *q= *p; *q = '\0'; /* In OSF/1 and Solaris the DAT device behaves like a 2 tm's for End of Tape */ #if !defined(__osf__) && !defined(__SVR4) && !defined(__svr4__) if ( getdev(host_dev) < 0 ) fcb->eotm = 1; #else getdev(host_dev); #endif } } else #if !defined(__osf__) && !defined(__SVR4) if ( getdev(devname) < 0 ) fcb->eotm = 1; #else getdev(devname); #endif /* Check if it is a remote host, so class="remote" */ if ( !no_host && osccomp(localhost,devhost,strlen(localhost)) ) { p = "remote"; oscopy(theclass.klass, p, 1+strlen(p)); oscopy(fcb->name, devname, 1+strlen(devname)); } if (fcb->eotm == 0) /* Default = 2 TM */ fcb->eotm = 2; if (hasam == 2) hasam = (fcb->eotm < 2); if (fcb->density <= 0) fcb->density = DEFAULT_DENSITY; if (fcb->blocksize <= 0) fcb->blocksize = 1; /* Tapes have this blocksize... */ /* Find to which Class the Device belongs to. */ if (getclass() < 0) goto error_found; /* Open the device. DON'T REWIND ! */ fcb->pfd = ioopen(fcb->name, ((mode == WRITE) ? READ_WRITE : mode), fcb->density); if (oserror) goto error_found; Units[i] = fcb; i += START_DEV; /* Actual returned unit number */ if (i >= ncb) ncb = i; fcb->last_op = U_OPEN; /* Last Operation */ /* Get Other Info about Device */ /* If ioinfo does not work drop eom jumps */ ioinfo(fcb->pfd, &stbuf, &fcb->file_no, &fcb->block_no); if (fcb->file_no == -1) hasam=0; /* If 2 FM drop the FCB_AM. It does not work properly */ /*if (fcb->eotm > 1) hasam=0;*/ if (isnulldev) oserror = 0; /* This can't be a problem */ /* Fill the missing items with final info */ if (fcb->ops[U_EOM] == osuerror) hasam = 0; if (stbuf.density) fcb->density = stbuf.density; if (hasbw) fcb->access |= FCB_BW; if (hasam) fcb->access |= FCB_AM; /* Take defaults for DA devices */ if (stbuf.isda) { fcb->access |= FCB_DA; if (stbuf.blocksize == 0) stbuf.blocksize = DEFAULT_SECTORSIZE; if (fcb->sectorsize == 0) fcb->sectorsize = den; if (fcb->sectorsize < stbuf.blocksize) fcb->sectorsize = stbuf.blocksize; if (fcb->usize == 0) fcb->usize = stbuf.usize/(fcb->sectorsize/stbuf.blocksize); if (fcb->usize == 0) fcb->usize = 0x7fffffff/fcb->sectorsize; if (fcb->blocksize <= 1) fcb->blocksize = fcb->sectorsize; } if (fcb->usize == 0) fcb->usize = stbuf.usize; if (stbuf.istm) fcb->access |= FCB_TM; /* * If the system doesn't know the position, rewind anyway. */ if (fcb->file_no < 0) { iorew(fcb->pfd); newfile(); } /* * Allocate test buffer */ if (!(test_buffer=(char *)osmmget(BUFTEST))) return(-1); /* block_no == 0, we are at the beggining of a file */ if (fcb->block_no == 0) { fcb->file_no -= 1; fcb->tape_marks = 1; } /* * Check position. To be sure we are not between 2 TM * or End_Of_Media, jump backwards 2 TM if possible. if (fcb->file_no > 0 && fcb->access & FCB_BW) bmove(2); */ oserror=0; /* Position to EOF if Append */ fcb->last_op = U_OPEN; /* Last Operation */ if ((mode&3) == APPEND) osufseek(i, 0, FILE_END); if (oserror) { Units[i-START_DEV] = NULL_PTR(FCB); goto error_found; } return(i); error_found: osmmfree((char *)fcb); osmmfree(test_buffer); return(-1); } int osuclose (f, option) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Close (dismount) a tape / optical disk .RETURNS 0 / -1 .REMARKS Tape is not rewinded... ----------------------------------------------------------------------*/ int f; /* IN: The unit to dismount */ int option; /* IN: option 1 to dismount the device (e.g. spin-down) */ { if (uget(f)) return(-1); /* Bad unit number... */ /* Perform necessary checks. Write tape marks if previous operation ** was write or fclose. */ if (fcb->access & FCB_DA) ; else { if (fcb->last_op == U_WRITE) tfclose(); if (fcb->last_op == U_WEOF) while (fcb->tape_marks < fcb->eotm) tfclose(); } if(oserror) return(-1); if (ioclose(fcb->pfd, (option == 1 ? OPU_DISMOUNT : 0))) return(-1); osmmfree((char *)fcb); osmmfree(test_buffer); Units[f-START_DEV] = NULL_PTR(FCB); return(0); } int osuread (f, buffer, length) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Read a block from magnetic tape / optical disk. .RETURNS Bytes read (>length if buffer too small) / 0 for EOF / -1 for error; omsg provides an explanation. .REMARKS The length MUST be a multiple of a sector size for optical disks. The returned length may be zero for virgin sectors. .REMARKS Reading consecutive file marks, it is posible to go further than two file marks. ----------------------------------------------------------------------*/ int f; /* IN: The file control block */ char *buffer; /* OUT: The buffer */ int length; /* IN: The length of the buffer */ { register int l; /* Check Buffer Length */ if (length < sizeof(long)) { oserror = -1; oserrmsg = "Too small buffer"; } if (uget(f)) /* Bad unit number... */ return(-1); /* ** If device was opened in only_write mode, return error ** if trying to read something */ if ((fcb->access & 3) == WRITE) { oserror=EACCES; return(-1); } /* ** Check if some required blocksize MUST be used. */ if (length%fcb->blocksize) { oserror = -1, oserrmsg = "Length not a multiple of Blocksize"; return(-1); } /* ** For Tapes, check for illegal read */ if (fcb->access & FCB_TM) { if ((fcb->last_op == U_WRITE) || (fcb->last_op == U_WEOF)) { oserror = -1; oserrmsg = "Can't read after write"; return(-1); } } /* ** For Direct-Access devices, use iosread (0 means "virgin sector") ** For Tapes, use ioread (0 means "tape-mark" ) */ if (fcb->access & FCB_DA) l = iosread(fcb->pfd, fcb->block_no, fcb->sectorsize, buffer, length); else { l = ioread(fcb->pfd,buffer,length); if (l == 0) { if (fcb->tape_marks && fcb->eotm > 1) { (fcb->tape_marks)++; bmove(1); fcb->status |= FCB_EOM; oserror = -1; oserrmsg = "End Of Data."; return(-1); } else (fcb->tape_marks)++; } } /* Check special case oserror = -2, means TOO SHORT BUFFER */ if (oserror == -2) { oserror = -1; oserrmsg = "Too short buffer"; newpos(length); return(-1); } /* ** If error, but last read was a FileMark then ** set EOM flag (End Of Media) and return -1. */ if (oserror) { if (fcb->tape_marks) { fcb->status |= FCB_EOM; oserror = -1; oserrmsg = "End Of Data."; } return(-1); } fcb->last_op = U_READ; if (l > 0) /* Something was read, update fcb contents */ newpos(l); return(l); } int osuwrite (f, buffer, length) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Write a block on magnetic tape / optical disk. .RETURNS Length written / -1 (error) .REMARKS ----------------------------------------------------------------------*/ int f; /* IN: The file control block */ char *buffer; /* IN: The buffer */ int length; /* IN: The length of the buffer */ { register int l; char *w_msg; int w_error; /* Check Buffer Length */ if (length < sizeof(long)) { oserror = -1; oserrmsg = "Too small buffer"; } if (uget(f)) /* Bad unit number... */ return(-1); /* Check if Write is Allowed */ if ((fcb->access & 3) == READ) { oserror = EACCES; return(-1); } /* ** Check if some required blocksize MUST be used. */ if (length%fcb->blocksize) { oserror = -1, oserrmsg = "Length not a multiple of Blocksize"; return(-1); } /* ** For Tapes, check if 2 TM were previously written; ** if yes, the data will not be accessible... */ if (fcb->access & FCB_TM) { if (fcb->tape_marks >= 2) { oserror = -1, oserrmsg = "Attempt to write beyond EOF"; return(-1); } } /* ** For Direct-Access devices, use ioswrite . */ if (fcb->access & FCB_DA) l = ioswrite(fcb->pfd, fcb->block_no, fcb->sectorsize, buffer, length); else l = iowrite(fcb->pfd,buffer,length); /* ** If Error when writing, the tape is positioned at the ** beginning of the file, where it was opened. */ if (l == -1) { w_error= oserror; w_msg = oserrmsg; if (fcb->access & FCB_BW) { bmove(1); tmove(1); } oserror = w_error; oserrmsg = w_msg; return(-1); } if (l > 0) newpos(l); fcb->last_op = U_WRITE; return(l); } int osufclose (f) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Close a File (Write a Tape-Mark on the tape) .RETURNS 0 / -1 .REMARKS Tape mouvements are optimized. Does nothing if file opened for read-only (returns wihout error) ----------------------------------------------------------------------*/ int f; /* IN: The file control block */ { if (uget(f)) /* Bad unit number... */ return(-1); /* Check if Write is Allowed */ if ((fcb->access & 3) == READ) return(0); if (fcb->last_op == U_WRITE) return(tfclose()); return(0); } long osufseek (f, offset, mode) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE File seek (Move the tape, i.e. find tape-marks). Identical to osubseek for disks. .RETURNS New file number .REMARKS On return, the tape is always positioned at a file beginning. The tape move is always stopped when two consecutive tape-marks are encountered. ----------------------------------------------------------------------*/ int f; /* IN: Unit Number */ long offset; /* IN : Offset for displacement */ int mode; /* IN : mode of addressing */ { register long fn; register int n, an, s1, s2; if (uget(f)) /* Bad unit number... */ return(-1); /* ** Perform necessary checks. Can't move tape after write... */ if (fcb->access & FCB_TM) { if (fcb->last_op == U_WRITE) { oserror = -1, oserrmsg = "Can't move after write"; return(-1); } } n = offset; fn = 0; switch(mode) { case FILE_END: if (n > 0) { oserror = ESPIPE; return(-1); } if (fcb->last_op == U_WEOF) { /* I just wrote a TM */ if ( (n == 0) && (fcb->tape_marks == 1)) break; while (fcb->tape_marks < fcb->eotm) tfclose(); } /* * Use fast EOM function if we can know where we are. * If ioeom ERROR, rewind and drops the FCB_AM flag */ if ( !(fcb->status & FCB_EOM) && (((n == 0 ) && (fcb->access & FCB_AM)) || ((fcb->access & FCB_AM) && (fcb->access & FCB_BW))) ) { s1=ioeom(fcb->pfd); s2=ioinfo(fcb->pfd,&stbuf,&fcb->file_no,&fcb->block_no); if (s1 == -1 || s2 == -1) { oserror = 0; rewind_dev(); fcb->access &= ~FCB_AM; } else { if (fcb->file_no == 0) rewind_dev(); else { fcb->file_no -= 1; fcb->tape_marks = 1; fcb->block_no = 0; fcb->status |= FCB_EOM; fcb->status &= ~FCB_NOCOUNT; if (n == 0) break; } } } while ( !(fcb->status & FCB_EOM) && (oserror == 0)) tmove(1); /* Find EOTape */ if ( n == 0 ) break; n += fcb->file_no + 1; /* n is now Absolute File Number where to go */ case FILE_START: if (n <= 0) { if (fcb->last_op == U_WEOF) /* I just wrote a TM */ while (fcb->tape_marks < fcb->eotm) tfclose(); rewind_dev(); break; } n -= (fcb->file_no + fcb->tape_marks); /* n is now Relative */ case FILE_CURRENT: an = n + fcb->file_no + fcb->tape_marks; /* Absolute File */ if (an <= 0) { if (fcb->last_op == U_WEOF) /* I just wrote a TM */ while (fcb->tape_marks < fcb->eotm) tfclose(); rewind_dev(); break; } if ( (n == 0) && (fcb->tape_marks)) /* I'm on a file beginning*/ break; if (fcb->last_op == U_WEOF) /* I just wrote a TM */ while (fcb->tape_marks < fcb->eotm) { tfclose(); n--;} if (n <= 0) { /* Move -n+1 backwards, 1 time forwards */ if (fcb->access & FCB_BW) { bmove(-n+1); tmove(1); n = 0; /* We're OK */ } else { /* First Rewind, then Move Forw. */ n = an; /* Where to go */ rewind_dev(); } } /* Move n tape-marks forwards */ /* * CG. The idea to position one by one in devices with 2 FM was * to avoid to jump after the last FileMark, something very * annoying particularly with 1/2" tapes. However this limit a * lot the fast positioning of DAT devices. if (fcb->eotm > 1) { while ((--n >= 0) && !(fcb->status & FCB_EOM)) tmove(1); if (n > 0) oserrmsg="File positioning error", oserror = -1; } else (tmove(n)); */ tmove(n); break; default: oserror = -1; oserrmsg = "Bad skip mode"; return(-1); } fn = fcb->file_no + fcb->tape_marks; return(oserror ? -1 : fn); } long osuftell (f) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Compute current file number .RETURNS Current file number ----------------------------------------------------------------------*/ int f; /* IN: Unit Number */ { register long fn; if (uget(f)) /* Bad unit number... */ return(-1); fn = fcb->file_no + fcb->tape_marks; return(fn); }