mirror of
https://github.com/open-simh/simtools.git
synced 2026-02-13 03:15:30 +00:00
Too much to list all, but includes (in no particular order): - Cleanup for 64-bit builds, MSVC warnings. - Structured help - Help file compiler. - Supports volsets, writes/create work. - Support for I18n in messages, help. - Makefiles. - Initialize volume/volset - Command line editing/history Builds and works on Linux and Windows (VS). Not recently built or tested on other platforms, but not intentinonally broken.
453 lines
14 KiB
C
453 lines
14 KiB
C
/* PHYUNIX.c Physical I/O module for Unix */
|
|
|
|
/*
|
|
* This is part of ODS2 written by Paul Nankervis,
|
|
* email address: Paulnank@au1.ibm.com
|
|
*
|
|
* ODS2 is distributed freely for all members of the
|
|
* VMS community to use. However all derived works
|
|
* must maintain comments in their source to acknowledge
|
|
* the contributions of the original author and
|
|
* subsequent contributors. This is free software; no
|
|
* warranty is offered, and while we believe it to be useful,
|
|
* you use it at your own risk.
|
|
*/
|
|
|
|
/*
|
|
* If the user mounts cd0 we open up /dev/cd0 for access.
|
|
*/
|
|
|
|
/******************************************************************************/
|
|
/* */
|
|
/* THE DEFINITION OF _FILE_OFFSET_BITS MUST APPEAR BEFORE ANY OTHER INCLUDES */
|
|
/* */
|
|
/* DO NOT MOVE THIS DEFINITION */
|
|
/* */
|
|
#ifndef _FILE_OFFSET_BITS /* */
|
|
#error _FILE_OFFSET_BITS must be defined in the makefile */
|
|
/* It MUST be consistent across all modules. */
|
|
#endif /* */
|
|
/* */
|
|
/******************************************************************************/
|
|
|
|
#if !defined( DEBUG ) && defined( DEBUG_PHYUNIX )
|
|
#define DEBUG DEBUG_PHYUNIX
|
|
#else
|
|
#ifndef DEBUG
|
|
#define DEBUG 0
|
|
#endif
|
|
#endif
|
|
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "cmddef.h"
|
|
#include "device.h"
|
|
#include "ods2.h"
|
|
#include "phyio.h"
|
|
#include "ssdef.h"
|
|
#include "phyvirt.h"
|
|
#include "compat.h"
|
|
|
|
#if defined(__digital__) && defined(__unix__)
|
|
#define DEV_PREFIX "/devices/rdisk/"
|
|
#else
|
|
#ifdef sun
|
|
#define DEV_PREFIX "/dev/dsk/"
|
|
#else
|
|
#define DEV_PREFIX "/dev/"
|
|
#endif
|
|
#endif
|
|
|
|
unsigned init_count = 0;
|
|
unsigned read_count = 0;
|
|
unsigned write_count = 0;
|
|
|
|
struct devdat {
|
|
char *name;
|
|
unsigned low;
|
|
unsigned high;
|
|
};
|
|
|
|
static vmscond_t phyio_read( struct DEV *dev, uint32_t block, uint32_t length,
|
|
char *buffer );
|
|
static vmscond_t phyio_write( struct DEV *dev, uint32_t block, uint32_t length,
|
|
const char *buffer );
|
|
static void showdevs( void );
|
|
static int devcmp( const void *d1, const void *d2 );
|
|
|
|
/*************************************************************** phyio_init() */
|
|
|
|
unsigned phyio_init( struct DEV *dev ) {
|
|
vmscond_t sts = SS$_NORMAL;
|
|
size_t n;
|
|
int fd, oflags, saverr = 0;
|
|
char *device;
|
|
struct stat statbuf;
|
|
|
|
init_count++;
|
|
|
|
dev->handle = -1;
|
|
dev->devread = phyio_read;
|
|
dev->devwrite = phyio_write;
|
|
|
|
if( dev->access & MOU_VIRTUAL ) {
|
|
const char *virtual;
|
|
|
|
virtual = virt_lookup( dev->devnam );
|
|
if ( virtual == NULL ) {
|
|
return SS$_NOSUCHDEV;
|
|
}
|
|
n = strlen( virtual );
|
|
device = (char *) malloc( n + 1 );
|
|
if( device == NULL )
|
|
return SS$_INSFMEM;
|
|
|
|
memcpy( device, virtual, n + 1 );
|
|
} else {
|
|
n = sizeof( DEV_PREFIX ) + strlen( dev->devnam );
|
|
device = (char *) malloc( n );
|
|
if ( device == NULL )
|
|
return SS$_INSFMEM;
|
|
|
|
memcpy( device, DEV_PREFIX, sizeof( DEV_PREFIX ) -1 );
|
|
memcpy( device+sizeof( DEV_PREFIX ) -1, dev->devnam, n +1 - sizeof( DEV_PREFIX ) );
|
|
device[n - 2] = '\0'; /* Remove ':' from device name */
|
|
}
|
|
|
|
oflags = (dev->access & MOU_WRITE )? O_RDWR : O_RDONLY;
|
|
if( (dev->access & (MOU_VIRTUAL|PHY_CREATE)) == (MOU_VIRTUAL|PHY_CREATE) )
|
|
oflags |= O_CREAT | O_EXCL;
|
|
|
|
fd = open( device, oflags, 0644 );
|
|
if( fd < 0 )
|
|
saverr = errno;
|
|
#if DEBUG
|
|
printf( "%d = open( \"%s\", %s%s )\n", fd, device,
|
|
(dev->access & MOU_WRITE )? "O_RDWR" : "O_RDONLY",
|
|
(dev->access & PHY_CREATE)? "|O_CREAT|O_EXCL, 0666" : "" );
|
|
#endif
|
|
if ( fd < 0 && (dev->access & (MOU_WRITE|PHY_CREATE)) == MOU_WRITE ) {
|
|
dev->access &= ~MOU_WRITE;
|
|
fd = open( device, O_RDONLY );
|
|
#if DEBUG
|
|
printf( "%d = open( \"%s\", O_RDONLY )\n", fd, device );
|
|
#endif
|
|
}
|
|
dev->handle = fd;
|
|
|
|
if( fd >= 0 ) {
|
|
if( fstat( fd, &statbuf ) == 0 ) {
|
|
if( dev->access & MOU_VIRTUAL ) {
|
|
if( !S_ISREG(statbuf.st_mode) ) {
|
|
sts = printmsg( IO_UNXNOTREG, 0, device );
|
|
close( fd );
|
|
dev->handle = -1;
|
|
} else
|
|
dev->eofptr = statbuf.st_size;
|
|
} else {
|
|
if( !S_ISBLK(statbuf.st_mode ) ) {
|
|
sts = printmsg( IO_UNXNOTBLK, 0, device );
|
|
close( fd );
|
|
dev->handle = -1;
|
|
}
|
|
}
|
|
} else {
|
|
if( !saverr )
|
|
saverr = errno;
|
|
close( fd );
|
|
dev->handle = -1;
|
|
fd = -1;
|
|
}
|
|
}
|
|
|
|
if ( fd < 0 ) {
|
|
switch( saverr ) {
|
|
case EEXIST:
|
|
printmsg( IO_EXISTS, 0, device );
|
|
sts = SS$_DUPFILENAME | STS$M_INHIB_MSG;
|
|
break;
|
|
default:
|
|
printmsg( IO_OPENDEV, 0, device );
|
|
printmsg( ODS2_OSERROR, MSG_CONTINUE, IO_OPENDEV, strerror( saverr ) );
|
|
sts = ((dev->access & MOU_VIRTUAL) ? SS$_NOSUCHFILE : SS$_NOSUCHDEV) |
|
|
STS$M_INHIB_MSG;
|
|
}
|
|
}
|
|
free( device );
|
|
|
|
return sts;
|
|
}
|
|
|
|
/*************************************************************** phyio_done() */
|
|
|
|
vmscond_t phyio_done( struct DEV *dev ) {
|
|
if( dev->handle != -1 )
|
|
close( dev->handle );
|
|
dev->handle = -1;
|
|
|
|
return SS$_NORMAL;
|
|
}
|
|
|
|
/*************************************************************** phyio_read() */
|
|
|
|
static vmscond_t phyio_read( struct DEV *dev, uint32_t block,
|
|
uint32_t length, char *buffer ) {
|
|
vmscond_t sts;
|
|
ssize_t res;
|
|
off_t pos;
|
|
|
|
#if DEBUG
|
|
printf("Phyio read block: %d into %p (%d bytes)\n",
|
|
block, buffer, length );
|
|
#endif
|
|
|
|
read_count++;
|
|
pos = (off_t) block * (off_t) 512;
|
|
if( (pos = lseek( dev->handle, pos, SEEK_SET )) < 0 ) {
|
|
int err;
|
|
|
|
err = errno;
|
|
printmsg( IO_SEEKERR, block );
|
|
sts = printmsg( ODS2_OSERROR, MSG_CONTINUE,
|
|
IO_SEEKERR, strerror(err) );
|
|
return sts;
|
|
}
|
|
if( (res = read( dev->handle, buffer, length )) != (ssize_t)length ) {
|
|
int err;
|
|
|
|
if( res == 0 ) {
|
|
return SS$_ENDOFFILE;
|
|
}
|
|
err = errno;
|
|
printmsg( IO_READERR, 0, block );
|
|
if( res == (off_t)-1 ) {
|
|
sts = printmsg( ODS2_OSERROR, MSG_CONTINUE,
|
|
IO_READERR, strerror(err) );
|
|
} else {
|
|
sts = printmsg( IO_READLEN, MSG_CONTINUE, IO_READERR,
|
|
block, (uint32_t)res, length );
|
|
}
|
|
return sts;
|
|
}
|
|
return SS$_NORMAL;
|
|
}
|
|
|
|
/************************************************************** phyio_write() */
|
|
|
|
static vmscond_t phyio_write( struct DEV *dev, uint32_t block,
|
|
uint32_t length, const char *buffer ) {
|
|
vmscond_t sts;
|
|
off_t pos;
|
|
ssize_t res;
|
|
|
|
#if DEBUG
|
|
printf("Phyio write block: %d from %p (%d bytes)\n",
|
|
block, buffer, length );
|
|
#endif
|
|
|
|
write_count++;
|
|
pos = (off_t) block * (off_t) 512;
|
|
if( ( pos = lseek( dev->handle, pos, SEEK_SET ) ) < 0 ) {
|
|
int err;
|
|
|
|
err = errno;
|
|
printmsg( IO_SEEKERR, block );
|
|
sts = printmsg( ODS2_OSERROR, MSG_CONTINUE,
|
|
IO_SEEKERR, strerror(err) );
|
|
return sts;
|
|
}
|
|
if( ( res = write( dev->handle, buffer, length ) ) != (ssize_t)length ) {
|
|
int err;
|
|
|
|
err = errno;
|
|
printmsg( IO_WRITEERR, 0, block );
|
|
if( res == (off_t)-1 ) {
|
|
sts = printmsg( ODS2_OSERROR, MSG_CONTINUE,
|
|
IO_WRITEERR, strerror(err) );
|
|
} else {
|
|
sts = printmsg( IO_WRITELEN, MSG_CONTINUE, IO_WRITEERR,
|
|
block, (uint32_t)res, length );
|
|
}
|
|
return sts;
|
|
}
|
|
return SS$_NORMAL;
|
|
}
|
|
|
|
/*************************************************************** phyio_help() */
|
|
|
|
void phyio_help( void ) {
|
|
char devpfx[] = { DEV_PREFIX };
|
|
|
|
devpfx[ sizeof( devpfx ) -2 ] = '\0'; /* Remove trailing '/' */
|
|
|
|
(void) helptopic( 0, "MOUNT UNIX", devpfx, DEV_PREFIX "cdrom0" );
|
|
|
|
return;
|
|
}
|
|
|
|
/*************************************************************** phyio_show() */
|
|
|
|
void phyio_show( showtype_t type ) {
|
|
switch( type ) {
|
|
case SHOW_STATS:
|
|
printmsg( IO_STATS, MSG_TEXT, init_count, read_count, write_count );
|
|
return;
|
|
case SHOW_FILE64:
|
|
printmsg( (sizeof( off_t ) < 8)? IO_NOLARGEFILE: IO_LARGEFILE, MSG_TEXT );
|
|
return;
|
|
case SHOW_DEVICES:
|
|
printmsg( IO_UNXDEVHDR, MSG_TEXT );
|
|
showdevs();
|
|
return;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
/*************************************************************** showdevs() */
|
|
static void showdevs( void ) {
|
|
DIR *dirp;
|
|
size_t i, cpl, n = 0, max = 0;
|
|
struct devdat (*list)[1] = NULL;
|
|
|
|
errno = 0;
|
|
if( (dirp = opendir( DEV_PREFIX )) != NULL ) {
|
|
do {
|
|
int nml;
|
|
size_t len;
|
|
unsigned unit;
|
|
char *fs;
|
|
struct dirent *dp;
|
|
struct devdat (*nl)[1];
|
|
struct stat stb;
|
|
|
|
errno = 0;
|
|
if( (dp = readdir(dirp)) == NULL )
|
|
break;
|
|
|
|
len = strlen( dp->d_name );
|
|
if( (fs = malloc( sizeof( DEV_PREFIX ) + len )) == NULL )
|
|
break;
|
|
memcpy( fs, DEV_PREFIX, sizeof( DEV_PREFIX ) -1 );
|
|
memcpy( fs + sizeof( DEV_PREFIX ) -1, dp->d_name, len +1 );
|
|
|
|
if( stat( fs, &stb ) == -1 ) {
|
|
int err;
|
|
|
|
err = errno;
|
|
printmsg( IO_DIRRDERR, 0, fs );
|
|
printmsg( ODS2_OSERROR, MSG_CONTINUE, IO_DIRRDERR, strerror( err ) );
|
|
free( fs );
|
|
continue;
|
|
}
|
|
if( !S_ISBLK( stb.st_mode ) ) {
|
|
free( fs );
|
|
continue;
|
|
}
|
|
|
|
memmove( fs, fs + sizeof( DEV_PREFIX ) -1, len +1 );
|
|
unit = ~0;
|
|
if( sscanf( fs, "%*[^0123456789]%n%u", &nml, &unit ) == 1 )
|
|
fs[nml] = '\0';
|
|
for( i = 0; i < n; i++ ) {
|
|
if( strcmp( (*list)[i].name, fs ) == 0 ) {
|
|
if( unit != ~0U ) {
|
|
if( unit > (*list)[i].high )
|
|
(*list)[i].high = unit;
|
|
if( unit < (*list)[i].low )
|
|
(*list)[i].low = unit;
|
|
}
|
|
free( fs );
|
|
break;
|
|
}
|
|
}
|
|
if( i >= n ) {
|
|
nl = (struct devdat (*)[1])
|
|
realloc( list, (n+1) * sizeof( struct devdat ) );
|
|
if( nl == NULL ) {
|
|
free( fs );
|
|
break;
|
|
}
|
|
list = nl;
|
|
(*list)[n].name = fs;
|
|
(*list)[n].low = unit;
|
|
(*list)[n++].high = unit;
|
|
}
|
|
} while( TRUE );
|
|
(void) closedir(dirp);
|
|
}
|
|
if( errno != 0 ) {
|
|
int err;
|
|
|
|
err = errno;
|
|
printmsg( IO_DIRRDERR, 0, DEV_PREFIX );
|
|
printmsg( ODS2_OSERROR, MSG_CONTINUE, IO_DIRRDERR, strerror( err ) );
|
|
for( i = 0; i < n; i++ )
|
|
free( (*list)[i].name );
|
|
free( list );
|
|
return;
|
|
}
|
|
|
|
if( n == 0 ) {
|
|
printmsg( IO_NODEVS, 0 );
|
|
return;
|
|
}
|
|
|
|
qsort( list, n, sizeof( struct devdat ), &devcmp );
|
|
|
|
for( i = 0; i < n; i++ ) {
|
|
char buf[128];
|
|
int p;
|
|
p = snprintf( buf, sizeof(buf), "%s", (*list)[i].name );
|
|
if( (*list)[i].low != ~0U ) {
|
|
p += snprintf( buf+p, sizeof(buf)-p, "%u", (*list)[i].low );
|
|
if( (*list)[i].high != (*list)[i].low )
|
|
p += snprintf( buf+p, sizeof(buf)-p, "-%u", (*list)[i].high );
|
|
}
|
|
if( p > (int)max )
|
|
max = p;
|
|
}
|
|
cpl = 50 / (max + 1);
|
|
if( cpl == 0 )
|
|
cpl = 1;
|
|
|
|
for( i = 0; i < n; i++ ) {
|
|
int p;
|
|
if( (i % cpl) == 0 ) {
|
|
if( i ) putchar( '\n' );
|
|
putchar( ' ' );
|
|
}
|
|
p = printf( " %s", (*list)[i].name ) -1;
|
|
free( (*list)[i].name );
|
|
|
|
if( (*list)[i].low != ~0U ) {
|
|
p += printf( "%u", (*list)[i].low );
|
|
if( (*list)[i].high != (*list)[i].low )
|
|
p += printf( "-%u", (*list)[i].high );
|
|
}
|
|
for( ; p < (int)max; p++ )
|
|
putchar( ' ' );
|
|
}
|
|
printf ( "\n" );
|
|
free( list );
|
|
|
|
return;
|
|
}
|
|
|
|
/*************************************************************** devcmp() */
|
|
|
|
static int devcmp( const void *d1, const void *d2 ) {
|
|
return strcmp( ((struct devdat *)d1)->name, ((struct devdat *)d2)->name );
|
|
}
|