/* FTLIB.C (C) Copyright TurboHercules, SAS 2010-2011 */ /* Hercules Tape Function Lib for FAKETAPE */ /* */ /* Released under "The Q Public License Version 1" */ /* (http://www.hercules-390.org/herclic.html) as modifications to */ /* Hercules. */ /*-------------------------------------------------------------------*/ /* This module contains the FAKETAPE emulated tape format library. */ /* Note: this library was built from the original FAKETAPE.C source. */ /*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/ /* Reference information: */ /* FSIMS100 Faketape manual */ /*-------------------------------------------------------------------*/ #include "hstdinc.h" #define _FTLIB_C_ #define _HTAPE_DLL_ #include "hercules.h" /* need Hercules control blocks */ #include "ftlib.h" /* Main faketape header file */ // Local constant data NOTE: Keep in sync with hetlib.c static const char *fet_errstr[] = { "No error", // FETE_OK "File error", // FETE_ERROR "Tapemark read", // FETE_TAPEMARK "Beginning of tape", // FETE_BOT "End of tape", // FETE_EOT "BOR not found", // FETE_BADBOR "EOR not found", // FETE_BADEOR "Unexpected tapemark", // FETE_BADMARK "Buffer not big enough", // FETE_OVERFLOW "Premature EOF", // FETE_PREMEOF "Decompression error", "Unknown compression method", "Compression error", "Specified length to big", // FETE_BADLEN "Write protected", // FETE_PROTECTED "Bad function code passed", // FETE_BADFUNC "Bad compression method", "Bad compression level", "Bad write chunk size", "Invalid direction specified", // FETE_BADDIR "Insufficient memory", // FETE_NOMEM "Couldn't read block header", // FETE_BADHDR "Inconsistent compression flags", "Block is short", // FETE_BADBLOCK "Location error", // FETE_BADLOC "Invalid error code", // unknown error }; #define FET_ERRSTR_MAX ( sizeof( fet_errstr) / sizeof( fet_errstr[ 0 ] ) ) /*-------------------------------------------------------------------*/ /* Open a FAKETAPE format file */ /* */ /* If successful, the file descriptor is stored in the device block */ /* and the return value is zero. Otherwise the return value is < 0. */ /*-------------------------------------------------------------------*/ DLL_EXPORT int fet_open ( FETB **fetb, char *filename, int flags ) { int rc = -1; /* Return code */ char pathname[MAX_PATH]; /* file path in host format */ FETB *tfetb; char *omode; int oflags; /* Open the FAKETAPE file */ *fetb = NULL; hostpath(pathname, filename, sizeof(pathname)); /* || Allocate a new FETB */ tfetb = calloc( 1, sizeof( FETB ) ); if( tfetb == NULL ) { return( FETE_NOMEM ); } /* Initialize FETB */ tfetb->fd = -1; /* || clear FETOPEN_CREATE if FETOPEN_READONLY is specified */ if(flags & FETOPEN_READONLY) { flags&=~FETOPEN_CREATE; } /* || Translate HET create flag to filesystem flag */ oflags = ( ( flags & FETOPEN_CREATE ) ? O_CREAT : 0 ); omode = "r+b"; if( !( flags & FETOPEN_READONLY ) ) { tfetb->fd = HOPEN( pathname, O_RDWR | O_BINARY | oflags, S_IRUSR | S_IWUSR | S_IRGRP ); } /* If file is read-only, attempt to open again */ if ( ( flags & FETOPEN_READONLY ) || (tfetb->fd < 0 && (EROFS == errno || EACCES == errno))) { omode = "rb"; tfetb->writeprotect = TRUE; tfetb->fd = HOPEN( pathname, O_RDONLY | O_BINARY, S_IRUSR | S_IRGRP ); } /* || Error out if both opens failed */ if( tfetb->fd == -1 ) { free( tfetb ); return( FETE_ERROR ); } /* || Associate stream with file descriptor */ tfetb->fh = fdopen( tfetb->fd, omode ); if( tfetb->fh == NULL ) { rc = errno; close( tfetb->fd ); errno = rc; free( tfetb ); return( FETE_ERROR ); } /* || If uninitialized tape, write 2 tapemarks to make it a valid NL tape */ rc = fet_read_header( tfetb, 0L, NULL, NULL ); if( rc < 0 && rc != FETE_TAPEMARK ) { if( rc != FETE_EOT ) { return( rc ); } rc = fet_tapemark( tfetb ); if( rc < 0 ) { return( rc ); } rc = fet_tapemark( tfetb ); if( rc < 0 ) { return( rc ); } tfetb->created = TRUE; } else tfetb->created = FALSE; /* || Reposition tape to load point */ rc = fet_rewind( tfetb ); if( rc < 0 ) { return( rc ); } /* || Give the caller the new FETB */ *fetb = tfetb; return( 0 ); } /* end function fet_open */ /*-------------------------------------------------------------------*/ /* Close a FAKETAPE format file */ /*-------------------------------------------------------------------*/ DLL_EXPORT int fet_close ( FETB **fetb ) { if( *(fetb) != NULL ) { if( (*fetb)->fh != NULL ) { fclose( (*fetb)->fh ); } free( *(fetb) ); } *fetb = NULL; return( 0 ); } /*-------------------------------------------------------------------*/ /* Read a FAKETAPE block header */ /* */ /* If successful, return value is zero, and prvblkl and curblkl are */ /* set to the previous and current block lengths respectively. */ /* If error, return value is -1 and unitstat is set to CE+DE+UC */ /* and prvblkl and curblkl are undefined. Either or both of prvblkl */ /* and/or curblkl may be NULL. */ /*-------------------------------------------------------------------*/ DLL_EXPORT int fet_read_header( FETB *fetb, off_t blkpos, U16* pprvblkl, U16* pcurblkl ) { int rc; /* Return code */ FAKETAPE_BLKHDR fakehdr; /* FakeTape block header */ char sblklen[5]; /* work for converting hdr */ int prvblkl; /* Previous block length */ int curblkl; /* Current block length */ int xorblkl; /* XOR check of block lens */ /* Reposition file to the requested block header */ rc = fseek( fetb->fh, blkpos, SEEK_SET ); if (rc < 0) { return FETE_ERROR; } /* Read the 12-ASCII-hex-character block header */ rc = (int)fread( &fakehdr, 1, sizeof(FAKETAPE_BLKHDR), fetb->fh ); /* Handle read error condition */ if (rc < 0) { return FETE_ERROR; } /* Handle end of file (uninitialized tape) condition */ if (rc == 0) { return FETE_EOT; } /* Handle end of file within block header */ if (rc < (int)sizeof(FAKETAPE_BLKHDR)) { return FETE_BADHDR; } /* Convert the ASCII-hex-character block lengths to binary */ strncpy( sblklen, fakehdr.sprvblkl, 4 ); sblklen[4] = 0; sscanf( sblklen, "%x", &prvblkl ); strncpy( sblklen, fakehdr.scurblkl, 4 ); sblklen[4] = 0; sscanf( sblklen, "%x", &curblkl ); strncpy( sblklen, fakehdr.sxorblkl, 4 ); sblklen[4] = 0; sscanf( sblklen, "%x", &xorblkl ); /* Verify header integrity using the XOR header field */ if ( (prvblkl ^ curblkl) != xorblkl ) { return FETE_ERROR; } /* Return the converted value(s) to the caller */ if (pprvblkl) *pprvblkl = prvblkl; if (pcurblkl) *pcurblkl = curblkl; /* Successful return */ return FETE_OK; } /* end function fet_read_header */ /*-------------------------------------------------------------------*/ /* Read a block from a FAKETAPE format file */ /* */ /* If successful, return value is block length read. */ /* If a tapemark was read, the return value is zero, and the */ /* current file number in the device block is incremented. */ /* If error, return value is < 0 */ /*-------------------------------------------------------------------*/ DLL_EXPORT int fet_read ( FETB *fetb, void *buf ) { int rc; /* Return code */ off_t blkpos; /* Offset of block header */ U16 curblkl; /* Current block length */ /* Initialize current block position */ blkpos = fetb->nxtblkpos; /* Read the block header to obtain the current block length */ rc = fet_read_header( fetb, blkpos, NULL, &curblkl ); if ( rc == FETE_EOT ) return FETE_EOT; if (rc < 0) return FETE_BADHDR; /* (error message already issued) */ /* Calculate the offset of the next block header */ blkpos += sizeof(FAKETAPE_BLKHDR) + curblkl; /* If not a tapemark, read the data block */ if (curblkl > 0) { rc = (int)fread( buf, 1, curblkl, fetb->fh ); /* Handle read error condition */ if (rc < 0) { return FETE_ERROR; } /* Handle end of file within data block */ if (rc < curblkl) { return FETE_BADBLOCK; } } /* Calculate the offsets of the next and previous blocks */ fetb->prvblkpos = fetb->nxtblkpos; fetb->nxtblkpos = blkpos; /* Increment the block number */ fetb->blockid++; /* Increment file number and return zero if tapemark was read */ if (curblkl == 0) { fetb->curfilen++; return FETE_TAPEMARK; /* UX will be set by caller */ } /* Return block length */ return curblkl; } /* end function fet_read */ /*-------------------------------------------------------------------*/ /* Write a FAKETAPE block header */ /* */ /* If successful, return value is zero. */ /* If error, return value is < 0 */ /*-------------------------------------------------------------------*/ DLL_EXPORT int fet_write_header ( FETB *fetb, off_t blkpos, U16 prvblkl, U16 curblkl ) { int rc; /* Return code */ FAKETAPE_BLKHDR fakehdr; /* FAKETAPE block header */ char temp[5]; /* Position file to where block header is to go */ rc = fseek( fetb->fh, blkpos, SEEK_SET ); if (rc < 0) { return FETE_BADLOC; } /* Build the 12-ASCII-hex-character block header */ MSGBUF( temp, "%4.4X", prvblkl ); memcpy( fakehdr.sprvblkl, temp, sizeof(fakehdr.sprvblkl)); MSGBUF( temp, "%4.4X", curblkl ); memcpy( fakehdr.scurblkl, temp, sizeof(fakehdr.scurblkl)); MSGBUF( temp, "%4.4X", prvblkl ^ curblkl ); memcpy( fakehdr.sxorblkl, temp, sizeof(fakehdr.sxorblkl)); /* Write the block header */ rc = (int)fwrite( &fakehdr, 1, sizeof(FAKETAPE_BLKHDR), fetb->fh ); if (rc < (int)sizeof(FAKETAPE_BLKHDR)) { if(errno==ENOSPC) { /* Disk FULL */ return FETE_EOT; } return FETE_ERROR; } return 0; } /* end function fet_write_header */ /*-------------------------------------------------------------------*/ /* Write a block to a FAKETAPE format file */ /* */ /* If successful, return value is zero. */ /* If error, return value is < 0 */ /*-------------------------------------------------------------------*/ DLL_EXPORT int fet_write( FETB *fetb, void *buf, U16 blklen ) { int rc; /* Return code */ off_t rcoff; /* Return code from lseek() */ off_t blkpos; /* Offset of block header */ U16 prvblkl; /* Length of previous block */ /* Initialize current block position and previous block length */ blkpos = fetb->nxtblkpos; prvblkl = 0; /* Determine previous block length if not at start of tape */ if (fetb->nxtblkpos > 0) { /* Retrieve the previous block length */ rc = fet_read_header( fetb, fetb->prvblkpos, NULL, &prvblkl ); if (rc < 0) return FETE_ERROR; /* Recalculate the offset of the next block */ blkpos = fetb->prvblkpos + sizeof(FAKETAPE_BLKHDR) + prvblkl; } /* Reposition file to the new block header */ rc = fseek( fetb->fh, blkpos, SEEK_SET ); if (rc < 0) { return FETE_BADLOC; } else rcoff = ftell( fetb->fh ); if(fetb->maxsize>0) { if((off_t)(fetb->nxtblkpos+blklen+sizeof(FAKETAPE_BLKHDR)) > fetb->maxsize ) { return FETE_EOT; } } /* Write the block header */ rc = fet_write_header( fetb, rcoff, prvblkl, blklen ); if (rc < 0) return FETE_EOT; /* (error message already issued) */ /* Calculate the offsets of the next and previous blocks */ fetb->nxtblkpos = blkpos + sizeof(FAKETAPE_BLKHDR) + blklen; fetb->prvblkpos = blkpos; /* Write the data block */ rc = (int)fwrite( buf, 1, blklen, fetb->fh); if (rc < blklen) { if(errno==ENOSPC) { /* Disk FULL */ return FETE_ERROR; } return FETE_EOT; } /* Increment the block number */ fetb->blockid++; /* Set new physical EOF */ do rc = ftruncate( fetb->fd, fetb->nxtblkpos ); while (EINTR == rc); if (rc != 0) { return FETE_EOT; } /* Return normal status */ return 0; } /* end function fet_write */ /*-------------------------------------------------------------------*/ /* Write a tapemark to a FAKETAPE format file */ /* */ /* If successful, return value is zero. */ /* If error, return value is < 0 */ /*-------------------------------------------------------------------*/ DLL_EXPORT int fet_tapemark( FETB *fetb ) { int rc; /* Return code */ off_t rcoff; /* Return code from lseek() */ off_t blkpos; /* Offset of block header */ U16 prvblkl; /* Length of previous block */ /* Initialize current block position and previous block length */ blkpos = fetb->nxtblkpos; prvblkl = 0; /* Determine previous block length if not at start of tape */ if (fetb->nxtblkpos > 0) { /* Retrieve the previous block length */ rc = fet_read_header( fetb, fetb->prvblkpos, NULL, &prvblkl ); if (rc < 0) return rc; /* Recalculate the offset of the next block */ blkpos = fetb->prvblkpos + sizeof(FAKETAPE_BLKHDR) + prvblkl; } /* Reposition file to the new block header */ rc = fseek( fetb->fh, blkpos, SEEK_SET ); if (rc < 0) { return FETE_BADLOC; } else rcoff = ftell( fetb->fh ); if(fetb->maxsize>0) { if((off_t)(fetb->nxtblkpos+sizeof(FAKETAPE_BLKHDR)) > fetb->maxsize) { return FETE_EOT; } } /* Write the block header */ rc = fet_write_header( fetb, rcoff, prvblkl, 0 ); if (rc < 0) return rc; /* (error message already issued) */ /* Increment the block number */ fetb->blockid++; /* Calculate the offsets of the next and previous blocks */ fetb->nxtblkpos = blkpos + sizeof(FAKETAPE_BLKHDR); fetb->prvblkpos = blkpos; /* Set new physical EOF */ rc = ftell( fetb->fh ); do rc = ftruncate( fetb->fd, fetb->nxtblkpos ); while (EINTR == rc); if (rc != 0) { return FETE_PROTECTED; } /* Return normal status */ return 0; } /* end function fet_tapemark */ /*-------------------------------------------------------------------*/ /* Synchronize a FAKETAPE format file (i.e. flush buffers to disk) */ /* */ /* If successful, return value is zero. */ /* If error, return value is < 0 */ /*-------------------------------------------------------------------*/ DLL_EXPORT int fet_sync( FETB *fetb ) { /* Unit check if tape is write-protected */ if (fetb->writeprotect) { return FETE_PROTECTED; } /* Perform sync. Return error on failure. */ if (fdatasync( fetb->fd ) < 0) { return FETE_ERROR; } /* Return normal status */ return 0; } /* end function fet_sync */ /*-------------------------------------------------------------------*/ /* Forward space over next block of a FAKETAPE format file */ /* */ /* If successful, return value is the length of the block skipped. */ /* If the block skipped was a tapemark, the return value is zero, */ /* and the current file number in the device block is incremented. */ /* If error, return value is < 0. */ /*-------------------------------------------------------------------*/ DLL_EXPORT int fet_fsb( FETB *fetb ) { int rc; /* Return code */ off_t blkpos; /* Offset of block header */ U16 blklen; /* Block length */ /* Initialize current block position */ blkpos = fetb->nxtblkpos; /* Read the block header to obtain the current block length */ rc = fet_read_header( fetb, blkpos, NULL, &blklen ); if (rc < 0) return rc; /* (error message already issued) */ /* Calculate the offset of the next block */ blkpos += sizeof(FAKETAPE_BLKHDR) + blklen; /* Calculate the offsets of the next and previous blocks */ fetb->prvblkpos = fetb->nxtblkpos; fetb->nxtblkpos = blkpos; /* Increment current file number if tapemark was skipped */ if (blklen == 0) fetb->curfilen++; /* Increment the block number */ fetb->blockid++; /* Return block length or zero if tapemark */ return blklen; } /* end function fet_fsb */ /*-------------------------------------------------------------------*/ /* Backspace to previous block of a FAKETAPE format file */ /* */ /* If successful, return value is the length of the block. */ /* If the block is a tapemark, the return value is zero, */ /* and the current file number in the device block is decremented. */ /* If error, return value is < 0 */ /*-------------------------------------------------------------------*/ DLL_EXPORT int fet_bsb( FETB *fetb ) { int rc; /* Return code */ U16 curblkl; /* Length of current block */ U16 prvblkl; /* Length of previous block */ off_t blkpos; /* Offset of block header */ /* Unit check if already at start of tape */ if (fetb->nxtblkpos == 0) { return FETE_BOT; } /* Backspace to previous block position */ blkpos = fetb->prvblkpos; /* Read the block header to obtain the block lengths */ rc = fet_read_header( fetb, blkpos, &prvblkl, &curblkl ); if (rc < 0) return rc; /* (error message already issued) */ /* Calculate the offset of the previous block */ fetb->prvblkpos = blkpos - sizeof(FAKETAPE_BLKHDR) - prvblkl; fetb->nxtblkpos = blkpos; /* Decrement current file number if backspaced over tapemark */ if (curblkl == 0) fetb->curfilen--; /* Decrement the block number */ fetb->blockid--; /* Return block length or zero if tapemark */ return curblkl; } /* end function fet_bsb */ /*-------------------------------------------------------------------*/ /* Forward space to next logical file of a FAKETAPE format file */ /* */ /* For FAKETAPE files, the forward space file operation is achieved */ /* by forward spacing blocks until positioned just after a tapemark. */ /* */ /* If successful, return value is zero, and the current file number */ /* in the device block is incremented by fet_fsb. */ /* If error, return value is < 0 */ /*-------------------------------------------------------------------*/ DLL_EXPORT int fet_fsf( FETB *fetb ) { int rc; /* Return code */ while (1) { /* Forward space over next block */ rc = fet_fsb( fetb ); if (rc < 0) return rc; /* (error message already issued) */ /* Exit loop if spaced over a tapemark */ if (rc == 0) break; } /* end while */ /* Return normal status */ return 0; } /* end function fet_fsf */ /*-------------------------------------------------------------------*/ /* Backspace to previous logical file of a FAKETAPE format file */ /* */ /* For FAKETAPE files, the backspace file operation is achieved */ /* by backspacing blocks until positioned just before a tapemark */ /* or until positioned at start of tape. */ /* */ /* If successful, return value is zero, and the current file number */ /* in the device block is decremented by fet_bsb. */ /* If error, return value is < 0 */ /*-------------------------------------------------------------------*/ DLL_EXPORT int fet_bsf( FETB *fetb ) { int rc; /* Return code */ while (1) { /* Exit if now at start of tape */ if (fetb->nxtblkpos == 0) { return FETE_BOT; } /* Backspace to previous block position */ rc = fet_bsb( fetb ); if (rc < 0) return rc; /* (error message already issued) */ /* Exit loop if backspaced over a tapemark */ if (rc == 0) break; } /* end while */ /* Return normal status */ return 0; } /* end function fet_bsf */ /*-------------------------------------------------------------------*/ /* Rewinds a FAKETAPE format file */ /*-------------------------------------------------------------------*/ DLL_EXPORT int fet_rewind ( FETB *fetb ) { int rc; rc = fseek( fetb->fh, 0L, SEEK_SET ); if ( rc < 0 ) { return FETE_ERROR; } fetb->nxtblkpos=0; fetb->prvblkpos=-1; fetb->curfilen=1; fetb->blockid=0; return 0; } /*-------------------------------------------------------------------*/ /* Determines if a FAKETAPE has passed a virtual EOT marker */ /*-------------------------------------------------------------------*/ DLL_EXPORT int fet_passedeot ( FETB *fetb ) { if(fetb->nxtblkpos==0) fetb->eotwarning = FALSE; else if(fetb->maxsize==0) fetb->eotwarning = TRUE; else if(fetb->nxtblkpos+fetb->eotmargin > fetb->maxsize) fetb->eotwarning = TRUE; else fetb->eotwarning = FALSE; return fetb->eotwarning; } DLL_EXPORT const char * fet_error( int rc ) { // no error, return OK if( rc > -1 ) rc = 0; rc *= -1; // make positive for indexing if( rc >= (int)FET_ERRSTR_MAX ) // detect mismatch errors vs error strings rc = FET_ERRSTR_MAX - 1; return( fet_errstr[ rc ] ); }