/* PHYVMS.c Physical I/O module for VMS */ /* * 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. */ /* How come VMS is so much easier to do physical I/O on than * those other ^%$@*! systems? I can't believe that they have * different command sets for different device types, and can * even have different command sets depending on what mode they * are called from! Sigh. */ /* Modified by: * * 31-AUG-2001 01:04 Hunter Goatley * * Added checks to be sure device we're mounting is a * disk and that it's already physically mounted at the * DCL level, * * 7-JUN-2005 Larry Baker * * If Mount/Virtual, use normal Unix open()/close()/read()/write(). * Negative handle -> Unix I/O; positive handle -> SYS$QIOW(). */ #if !defined( DEBUG ) && defined( DEBUG_PHYVMS ) #define DEBUG DEBUG_PHYVMS #else #ifndef DEBUG #define DEBUG 0 #endif #endif #include #include #include #include #include #include #include #include /* struct FAB */ #include #include #include #include #include #ifdef VAXC #define LIB$M_FIL_NOWILD 1 #include #include #else #ifdef VAX #define LIB$M_FIL_NOWILD 1 #else #include /* LIB$M_FIL_NOWILD */ #endif #include #include #endif #ifdef __GNUC__ unsigned sys$assign(); unsigned sys$close(); unsigned sys$open(); unsigned sys$qiow(); unsigned sys$dassgn(); #else #include #endif #ifndef SS$_NOMOREDEV #define SS$_NOMOREDEV 2648 #endif #ifndef EFN$C_ENF #define EFN$C_ENF 0 /* 128 in recent VMS */ #endif #include "ods2.h" #include "phyio.h" #include "phyvirt.h" 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 ); struct ITMLST { unsigned short length; unsigned short itmcod; void *buffer; unsigned short *retlen; }; #define MSGID(sts) ((sts) & (STS$M_COND_ID|STS$M_SEVERITY)) static unsigned init_count = 0; static unsigned read_count = 0; static unsigned write_count = 0; /*************************************************************** showdevs() */ static void showdevs( void ) { vmscond_t status; size_t max = 0; int pass; max = 0 for( pass = 0; pass < 2; pass++ ) { unsigned long ctx[2] = { 0, 0 }; size_t col = 0; unsigned short retlen, iosb[4]; char devnam[64+1], dspnam[64+1]; struct dsc$descriptor_s retdsc = { sizeof( devnam ) -1, DSC$K_DTYPE_T, DSC$K_CLASS_S, devnam }; $DESCRIPTOR( searchdsc, "*" ); unsigned long devclass = DC$_DISK; struct ITMLST scanlist[] = { { sizeof( devclass ), DC$_DISK, &devclass, NULL }, { 0, 0, NULL, NULL } }; struct ITMLST dvilist[] = { { sizeof( dspnam ) -1, DVI$_DISPLAY_DEVNAM, dspnam, &retlen }, { 0, 0, NULL, NULL } }; while( 1 ) { char *name; size_t len; status = sys$device_scan( &retdsc, &retlen, &searchdsc, scanlist, &ctx ); if( MSGID(status) != SS$_NORMAL ) break; name = devnam; name[retlen] = '\0'; len = retlen; status = sys$getdviw( EFN$C_ENF, 0, &retdsc, dvilist, iosb, NULL, 0, NULL ); if( MSGID(status) == SS$_NORMAL ) { name = dspnam; dspnam[retlen] = '\0'; len = retlen; } if( pass == 0 ) { if( len > max ) max = len; continue; } if( col + max > 75 ) { putchar( '\n' ); col = 0; } col += 1+ max; printf( " %-*s", (int)max, name ); } } switch( MSGID(status) ) { case SS$_NOMOREDEV: break; case SS$_NOSUCHDEV: printmsg( IO_NODEVS, MSG_TEXT ); break; case SS$_NORMAL: break; default: printmsg( IO_VMSDEVSCAN, 0 ); printmsg( status, MSG_CONTINUE, IO_VMSDEVSCAN ); break; } 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: printf( " Physical devices\n" ); showdevs(); return; default: abort(); } } /*************************************************************** phyio_help() */ void phyio_help(FILE *fp ) { (void) helptopic( 0, "MOUNT VMS" ); return; } /*************************************************************** phyio_init() */ vmscond_t phyio_init( struct DEV *dev ) { vmscond_t sts; const char *virtual; init_count++; dev->handle = -1; dev->devread = phyio_read; dev->devwrite = phyio_write; dev->sectors = 0; if( dev->access & MOU_VIRTUAL ) { int vmsfd; struct FAB fab; virtual = virt_lookup( dev->devnam ); if ( virtual == NULL ) { return SS$_NOSUCHDEV; } #define opnopts "ctx=stm", "rfm=udf", "shr=get,put" vmsfd = open( virtual, ((dev->access & MOU_WRITE )? O_RDWR : O_RDONLY) | ((dev->access & (MOU_VIRTUAL|PHY_CREATE)) == (MOU_VIRTUAL|PHY_CREATE)? O_CREAT | O_EXCL : 0), 0666, opnopts ); if ( vmsfd < 0 && (dev->access & (MOU_WRITE|PHY_CREATE)) == MOU_WRITE ) { dev->access &= ~MOU_WRITE; vmsfd = open( virtual, O_RDONLY, 0444, opnopts ); } if ( vmsfd < 0 ) { int err; switch( errno ) { case EEXIST: printmsg( IO_EXISTS, 0, virtual ); return SS$_DUPFILENAME | STS$M_INHIB_MSG; default: err = errno; printmsg( IO_OPENDEV, 0, virtual ); return printmsg( ODS2_OSERROR, MSG_CONTINUE, IO_OPENDEV, strerror( err ) ); } } dev->handle = vmsfd; fab = cc$rms_fab; /* Make this a real FAB (bid and bln) */ fab.fab$l_fna = (char *) virtual; fab.fab$b_fns = strlen( virtual ); if( $SUCCESSFUL(sts = sys$open( &fab )) {/* Lookup file, get file size */ dev->sectors = fab.fab$l_alq; sys$close( &fab ); if ( sizeof( off_t ) < 8 && dev->sectors > 0x3FFFFF ) { close( vmsfd ); dev->handle = -1; return printmsg( IO_OFFSET_TOO_BIG, 0 ); /* Need 64-bit off_t */ } } } else { /* Physical */ struct dsc$descriptor devdsc; unsigned long devclass, cylinders, tracks, sectors; char devname[65]; unsigned long devchar; unsigned short iosb[4]; struct ITMLST dvi_itmlst[7] = { { sizeof( devclass ), DVI$_DEVCLASS, &devclass, NULL }, { sizeof( devname ), DVI$_ALLDEVNAM, &devname, NULL }, { sizeof( devchar ), DVI$_DEVCHAR, &devchar, NULL }, { sizeof( cylinders ), DVI$_CYLINDERS, &cylinders, NULL }, { sizeof( tracks ), DVI$_TRACKS, &tracks, NULL }, { sizeof( sectors ), DVI$_SECTORS, §ors, NULL }, { 0, 0, 0, 0 } }; /* Get some device information: device name, class, and mount status */ memset( devname, 0, sizeof( devname ) ); devdsc.dsc$w_length = strlen( dev->devnam ); devdsc.dsc$a_pointer = dev->devnam; if( $FAILS(sts = sys$getdviw( EFN$C_ENF, 0, &devdsc, &dvi_itmlst, iosb, 0, 0, 0 )) ) { return sts; } if( devclass != DC$_DISK ) { /* If not a disk, return an error */ return SS$_IVDEVNAM; } dev->sectors = cylinders * tracks * sectors; if( !(devchar & DEV$M_MNT) ) { #if 1 /* * This code will mount the disk if it's not already mounted. * If you enable it, the "mount" command will mount the disk, but * it'll stay mounted by the process running ODS2 if ODS2 exits abnormally. */ unsigned long mntflags[2]; struct ITMLST mnt_itmlst[3] = { { 0, MNT$_DEVNAM, &devname, NULL }, { sizeof( mntflags ), MNT$_FLAGS, &mntflags, NULL }, { 0, 0, 0, 0 } }; mnt_itmlst[0].length = strlen( devname ); mntflags[0] = MNT$M_FOREIGN | MNT$M_NOASSIST | MNT$M_NOMNTVER; if( !(dev->access & MOU_WRITE) ) { mntflags[0] |= MNT$M_NOWRITE; } mntflags[1] = 0; sts = sys$mount( &mnt_itmlst ); if( $FAILED(sts) && (dev->access & MOU_WRITE) ) { dev->access &= ~MOU_WRITE; mntflags[0] |= MNT$M_NOWRITE; sts = sys$mount( &mnt_itmlst ); } if( $SUCCESSFUL(sts) ) { dev->access |= OS_MOUNTED; printmsg( IO_VMSMOUNT, 0, devname ); } else { printmsg( IO_VMSNOMOUNT, 0, devname ); } #else sts = SS$_DEVNOTMOUNT; #endif } if( $SUCCESSFUL(sts) ) { sts = sys$assign( &devdsc, &dev->handle, 0, 0, 0, 0 ); } } return sts; } /*************************************************************** phyio_done() */ vmscond_t phyio_done( struct DEV *dev ) { vmscond_t status; status = SS$_NORMAL; if ( dev->access & MOU_VIRTUAL ) { close( dev->handle ); virt_device( dev->devnam, NULL ); } else { #if 1 if( dev->access & OS_MOUNTED ) { struct dsc$descriptor devdsc; devdsc.dsc$b_dtype = DSC$K_DTYPE_T; devdsc.dsc$b_class = DSC$K_CLASS_S; devdsc.dsc$w_length = strlen( dev->devnam ); devdsc.dsc$a_pointer = dev->devnam; if( $SUCCESSFUL(status = sys$dismou( &devdsc, 0 )) ) { printmsg( IO_VMSDMT, 0, dev->devnam ); } else { printmsg( IO_VMSNODMT, 0, dev->devnam ); } dev->access &= ~OS_MOUNTED; } #endif sys$dassgn( dev->handle ); } dev->handle = 0; return status; } /*************************************************************** phyio_read() */ static vmscond_t phyio_read( struct DEV *dev, uint32_t block, uint32_t length, char *buffer ) { vmscond_t sts; #if DEBUG printf("Phyio read block: %d into %x (%d bytes)\n",block,buffer,length); #endif read_count++; if( dev->access & MOU_VIRTUAL ) { int res; if( (res = lseek( dev->handle, block * 512, 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 )) != 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; } else { unsigned short int iosb[4]; return sys$qiow( EFN$C_ENF, dev->handle, IO$_READLBLK, iosb, 0, 0, buffer, length, block, 0, 0, 0 ); } } /************************************************************** phyio_write() */ static vmscond_t phyio_write( struct DEV *dev, uint32_t block, uint32_t length, const char *buffer ) { vmscond_t sts; #if DEBUG printf("Phyio write block: %d from %x (%d bytes)\n",block,buffer,length); #endif write_count++; if( dev->access & MOU_VIRTUAL ) { int res; if( (res = lseek( dev->handle, block * 512, 0 )) < 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 )) != 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; } else { unsigned short int iosb[4]; return sys$qiow( EFN$C_ENF, dev->handle, IO$_WRITELBLK, iosb, 0, 0, buffer, length, block, 0, 0, 0 ); } }