/* 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 #include #include #include #include #include #include #include #include #include #include #include #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 ); }