/* Access.c */ /* * 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. */ /* * This module implements 'accessing' files on an ODS2 * disk volume. It uses its own low level interface to support * 'higher level' APIs. For example it is called by the * 'RMS' routines. */ #if !defined( DEBUG ) && defined( DEBUG_ACCESS ) #define DEBUG DEBUG_ACCCESS #else #ifndef DEBUG #define DEBUG 0 #endif #endif #include #include #include #include #include #include "ssdef.h" #include "access.h" #include "device.h" #include "initvol.h" #include "ods2.h" #include "phyvirt.h" #include "stsdef.h" #include "compat.h" #include "sysmsg.h" #include "vmstime.h" extern vmscond_t mountdef( const char *devnam ); struct VCB *vcb_list = NULL; /* WCBKEY passes info to compare/create routines */ struct WCBKEY { unsigned vbn; struct FCB *fcb; struct WCB *prevwcb; }; static vmscond_t get_scb( struct VCB *vcb, unsigned device, struct SCB *scb ); #define DEBUGx /***************************************************************** checksum() */ /* checksum() to produce header checksum values... */ f11word checksumn( f11word *block, int count ) { register unsigned result; register unsigned short *ptr; register unsigned data; ptr = block; result = 0; for ( ; count > 0; --count ) { data = *ptr++; result += F11WORD(data); } return (f11word)(result & 0xFFFF); } f11word checksum( f11word *block ) { return checksumn( block, 255 ); } /***************************************************************** fid_copy() */ /* fid_copy() copy fid from file header with default rvn! */ void fid_copy( struct fiddef *dst, struct fiddef *src, unsigned rvn ) { dst->fid$w_num = F11WORD(src->fid$w_num); dst->fid$w_seq = F11WORD(src->fid$w_seq); dst->fid$b_rvn = src->fid$b_rvn == 0 ? rvn : src->fid$b_rvn; dst->fid$b_nmx = src->fid$b_nmx; } /************************************************************* deaccesshead() */ /* deaccesshead() release header from INDEXF... */ vmscond_t deaccesshead( struct VIOC *vioc, struct HEAD *head, uint32_t idxblk ) { f11word check; if ( head && idxblk ) { check = checksum( (f11word *) head ); head->fh2$w_checksum = F11WORD(check); } return deaccesschunk( vioc, idxblk, 1, TRUE ); } /*************************************************************** accesshead() */ /* accesshead() find file or extension header from INDEXF... */ vmscond_t accesshead( struct VCB *vcb, struct fiddef *fid, unsigned seg_num, struct VIOC **vioc, struct HEAD **headbuff, uint32_t *retidxblk, unsigned wrtflg ) { register vmscond_t sts; register struct VCBDEV *vcbdev; register uint32_t idxblk; vcbdev = RVN_TO_DEV(vcb, fid->fid$b_rvn); if( vcbdev == NULL ) return SS$_DEVNOTMOUNT; if( wrtflg && !(vcb->status & VCB_WRITE) ) return SS$_WRITLCK; idxblk = fid->fid$w_num + (fid->fid$b_nmx << 16) - 1 + F11WORD(vcbdev->home.hm2$w_ibmapvbn) + F11WORD(vcbdev->home.hm2$w_ibmapsize); if (vcbdev->idxfcb->head != NULL) { if (idxblk >= F11SWAP(vcbdev->idxfcb->head->fh2$w_recattr.fat$l_efblk)) { #if DEBUG printf("%u,%u,%u, %u Not in index file\n", fid->fid$w_num, fid->fid$w_seq, fid->fid$b_rvn, fid->fid$b_nmx); #endif return SS$_NOSUCHFILE; } } if( $SUCCESSFUL(sts = accesschunk( vcbdev->idxfcb, idxblk, vioc, (char **) headbuff, NULL, wrtflg ? 1 : 0 )) ) { register struct HEAD *head = *headbuff; if( retidxblk ) { *retidxblk = wrtflg ? idxblk : 0; } if (F11WORD(head->fh2$w_fid.fid$w_num) != fid->fid$w_num || head->fh2$w_fid.fid$b_nmx != fid->fid$b_nmx || F11WORD(head->fh2$w_fid.fid$w_seq) != fid->fid$w_seq || (head->fh2$w_fid.fid$b_rvn != fid->fid$b_rvn && head->fh2$w_fid.fid$b_rvn != 0)) { sts = SS$_NOSUCHFILE; } else if( head->fh2$b_idoffset < 38 || head->fh2$b_idoffset > head->fh2$b_mpoffset || head->fh2$b_mpoffset > head->fh2$b_acoffset || head->fh2$b_acoffset > head->fh2$b_rsoffset || head->fh2$b_map_inuse > head->fh2$b_acoffset - head->fh2$b_mpoffset || checksum( (f11word *) head ) != F11WORD( head->fh2$w_checksum ) ) { #if DEBUG printf( "--->accesshead(): File header checksum failure:" ); printf( " FH2$W_CHECKSUM=%u, checksum()=%u\n", F11WORD( head->fh2$w_checksum ), checksum( (f11word *) head ) ); #endif sts = $SETLEVEL(SS$_BADCHKSUM, SEVERE); } else if( F11WORD(head->fh2$w_seg_num) != seg_num ) { sts = SS$_FILESEQCHK; } if( $FAILED(sts) ) { deaccesschunk( *vioc, 0, 0, FALSE ); } } return sts; } /************************************************************** wcb_compare() */ /* wcb_compare() compare two windows - return -1 for less, 0 for match */ /* as a by product keep highest previous entry so that if a new window * is required we don't have to go right back to the initial file header */ static int wcb_compare( uint32_t hashval, void *keyval, void *thiswcb ) { register struct WCBKEY *wcbkey; register struct WCB *wcb; UNUSED(hashval); wcbkey = (struct WCBKEY *) keyval; wcb = (struct WCB *) thiswcb; if( wcbkey->vbn < wcb->loblk ) { return -1; /* Search key is less than this window maps */ } if (wcbkey->vbn <= wcb->hiblk) { return 0; /* Search key must be in this window */ } if (wcbkey->prevwcb == NULL) { wcbkey->prevwcb = wcb; } else { if (wcb->loblk != 0 && wcb->hiblk > wcbkey->prevwcb->hiblk) { wcbkey->prevwcb = wcb; } } return 1; /* Search key is higher than this window... */ } /************************************************************ premap_indexf() */ /* premap_indexf() called to physically read the header for indexf.sys * so that indexf.sys can be mapped and read into virtual cache.. */ struct HEAD *premap_indexf( struct FCB *fcb, vmscond_t *retsts ) { struct HEAD *head; struct VCBDEV *vcbdev; int iderr; vcbdev = RVN_TO_DEV( fcb->vcb, fcb->rvn ); if( vcbdev == NULL ) { *retsts = SS$_DEVNOTMOUNT; return NULL; } head = (struct HEAD *) malloc(sizeof(struct HEAD)); if( head == NULL ) { *retsts = SS$_INSFMEM; return NULL; } if( $FAILS(*retsts = virt_read( vcbdev->dev, F11LONG( vcbdev->home.hm2$l_ibmaplbn ) + F11WORD( vcbdev->home.hm2$w_ibmapsize ), sizeof( struct HEAD ), (char *) head )) ) { free( head ); return NULL; } if( (iderr = (F11WORD(head->fh2$w_fid.fid$w_num) != FID$C_INDEXF || head->fh2$w_fid.fid$b_nmx != 0 || F11WORD(head->fh2$w_fid.fid$w_seq) != FID$C_INDEXF)) || F11WORD(head->fh2$w_checksum) != checksum( (f11word *) head ) ) { #if DEBUG printf( "--->premap_indexf(): Index file header %s" ); printf( " failure: FH2$W_CHECKSUM=%u, checksum()=%u\n", (iderr? "File ID match" : "checksum"), F11WORD( head->fh2$w_checksum ), checksum( (f11word *) head ) ); #endif *retsts = $SETLEVEL((iderr? SS$_FILENUMCHK: SS$_BADCHKSUM), SEVERE); free( head ); return NULL; } return head; } /*************************************************************** wcb_create() */ /* wcb_create() creates a window control block by reading appropriate * file headers... */ static void *wcb_create( uint32_t hashval, void *keyval, vmscond_t *retsts ) { register struct WCB *wcb; uint32_t curvbn; unsigned extents = 0; struct HEAD *head; struct VIOC *vioc = NULL; register struct WCBKEY *wcbkey; UNUSED(hashval); wcb = (struct WCB *) calloc( sizeof(struct WCB), 1 ); if( wcb == NULL ) { *retsts = SS$_INSFMEM; return NULL; } wcbkey = (struct WCBKEY *) keyval; wcb->cache.objmanager = NULL; wcb->cache.objtype = OBJTYPE_WCB; if( wcbkey->prevwcb == NULL ) { curvbn = wcb->loblk = 1; wcb->hd_seg_num = 0; head = wcbkey->fcb->head; if( head == NULL ) { head = premap_indexf( wcbkey->fcb, retsts ); if (head == NULL) { free( wcb ); return NULL; } head->fh2$w_ext_fid.fid$w_num = 0; head->fh2$w_ext_fid.fid$b_nmx = 0; } fid_copy(&wcb->hd_fid, &head->fh2$w_fid, wcbkey->fcb->rvn); } else { wcb->loblk = wcbkey->prevwcb->hiblk + 1; curvbn = wcbkey->prevwcb->hd_basevbn; wcb->hd_seg_num = wcbkey->prevwcb->hd_seg_num; memcpy( &wcb->hd_fid, &wcbkey->prevwcb->hd_fid, sizeof(struct fiddef) ); head = wcbkey->fcb->head; } while( TRUE ) { register f11word *mp; register f11word *me; wcb->hd_basevbn = curvbn; if( wcb->hd_seg_num != 0 ) { if( $FAILS(*retsts = accesshead(wcbkey->fcb->vcb, &wcb->hd_fid, wcb->hd_seg_num, &vioc, &head, NULL, 0)) ) { free(wcb); return NULL; } } mp = (f11word *) head + head->fh2$b_mpoffset; me = mp + head->fh2$b_map_inuse; while( mp < me ) { register uint32_t phylen, phyblk; register f11word mp0 = 0; switch( ((mp0 = F11WORD(*mp)) >> 14) & 0x3 ) { case 0: phylen = 0; mp++; break; case 1: phylen = (mp0 & 0x00ff) + 1; phyblk = ((mp0 & 0x3f00) << 8) | F11WORD(mp[1]); mp += 2; break; case 2: phylen = (mp0 & 0x3fff) + 1; phyblk = (F11WORD(mp[2]) << 16) | F11WORD(mp[1]); mp += 3; break; case 3: default: phylen = ((mp0 & 0x3fff) << 16) + F11WORD(mp[1]) + 1; phyblk = (F11WORD(mp[3]) << 16) | F11WORD(mp[2]); mp += 4; break; } curvbn += phylen; if( phylen != 0 && curvbn > wcb->loblk ) { wcb->phylen[extents] = phylen; wcb->phyblk[extents] = phyblk; wcb->rvn[extents] = wcb->hd_fid.fid$b_rvn; if( ++extents >= EXTMAX) { if( curvbn > wcbkey->vbn ) { break; } else { extents = 0; wcb->loblk = curvbn; } } } } if( extents >= EXTMAX || (F11WORD(head->fh2$w_ext_fid.fid$w_num) == 0 && head->fh2$w_ext_fid.fid$b_nmx == 0) ) { break; } { register unsigned rvn; wcb->hd_seg_num++; rvn = wcb->hd_fid.fid$b_rvn; fid_copy(&wcb->hd_fid, &head->fh2$w_ext_fid, rvn); if( vioc != NULL ) { deaccesshead(vioc, NULL, 0); vioc = NULL; } } } if( vioc != NULL ) { deaccesshead(vioc, NULL, 0); vioc = NULL; } else { if( wcbkey->fcb->head == NULL ) free(head); } wcb->hiblk = curvbn - 1; wcb->extcount = extents; *retsts = SS$_NORMAL; if( curvbn <= wcbkey->vbn ) { free(wcb); wcb = NULL; #if DEBUG printf( "--->wcb_create(): curvbn (=%u) <= wcbkey->vbn (=%u)\n", curvbn, wcbkey->vbn ); #endif *retsts = SS$_DATACHECK; } return wcb; } /**************************************************************** getwindow() */ /* getwindow() find a window to map VBN to LBN ... */ vmscond_t getwindow( struct FCB * fcb, uint32_t vbn, struct VCBDEV **devptr, uint32_t *phyblk, uint32_t *phylen, struct fiddef *hdrfid, unsigned *hdrseq ) { vmscond_t sts; struct WCB *wcb; struct WCBKEY wcbkey; register unsigned extent = 0; register unsigned togo; #if DEBUG printf("Accessing window for vbn %u, file (%u)\n", vbn, fcb->cache.hashval); #endif memset( &wcbkey, 0, sizeof( wcbkey ) ); wcbkey.vbn = vbn; wcbkey.fcb = fcb; wcbkey.prevwcb = NULL; wcb = cache_find( (void *) &fcb->wcb, 0, &wcbkey, &sts, wcb_compare, wcb_create ); if( wcb == NULL ) return sts; togo = vbn - wcb->loblk; while( togo >= wcb->phylen[extent] ) { togo -= wcb->phylen[extent]; if( ++extent > wcb->extcount ) return SS$_BUGCHECK; } *devptr = RVN_TO_DEV(fcb->vcb, wcb->rvn[extent]); *phyblk = wcb->phyblk[extent] + togo; *phylen = wcb->phylen[extent] - togo; if( hdrfid != NULL ) memcpy(hdrfid, &wcb->hd_fid, sizeof(struct fiddef)); if( hdrseq != NULL ) *hdrseq = wcb->hd_seg_num; #if DEBUG printf("Mapping vbn %u to %u (%u -> %u)[%u] file (%u)\n", vbn, *phyblk, wcb->loblk, wcb->hiblk, wcb->hd_basevbn, fcb->cache.hashval); #endif cache_untouch( &wcb->cache, TRUE ); if( *devptr == NULL ) return SS$_DEVNOTMOUNT; return SS$_NORMAL; } /************************************************************* vioc_manager() */ /* Object manager for VIOC objects:- if the object has been * modified then we need to flush it to disk before we let * the cache routines do anything to it... */ void *vioc_manager( struct CACHE * cacheobj, int flushonly ) { register struct VIOC *vioc; UNUSED(flushonly); vioc = (struct VIOC *) cacheobj; if( vioc->modmask != 0 ) { register struct FCB *fcb = NULL; register unsigned int length = VIOC_CHUNKSIZE; register uint32_t curvbn = 0; register char *address = NULL; register unsigned modmask = 0; fcb = vioc->fcb; curvbn = (size_t)vioc->cache.hashval + 1; address = (char *) vioc->data; modmask = vioc->modmask; #if DEBUG printf("\nvioc_manager writing vbn %d\n", curvbn); #endif do { register vmscond_t sts; unsigned int wrtlen = 0; uint32_t phyblk = 0, phylen = 0, idxlbn; struct VCBDEV *vcbdev = NULL; while( length > 0 && !(1 & modmask) ) { length--; curvbn++; address += 512; modmask = modmask >> 1; } while (wrtlen < length && (1 & modmask) ) { wrtlen++; modmask = modmask >> 1; } length -= wrtlen; while( wrtlen > 0 ) { if( fcb->highwater != 0 && curvbn >= fcb->highwater ) { length = 0; break; } if( $FAILS(sts = getwindow(fcb, curvbn, &vcbdev, &phyblk, &phylen, NULL, NULL)) ) return NULL; if (phylen > wrtlen) phylen = wrtlen; if (fcb->highwater != 0 && curvbn + phylen > fcb->highwater) { phylen = fcb->highwater - curvbn; } if( $FAILS(sts = virt_write( vcbdev->dev, phyblk, phylen * 512, address )) ) return NULL; idxlbn = (F11LONG(vcbdev->home.hm2$l_ibmaplbn) + F11WORD(vcbdev->home.hm2$w_ibmapsize)); if( idxlbn >= phyblk && idxlbn < phyblk + phylen ) { if( $FAILS(sts = virt_write( vcbdev->dev, F11LONG(vcbdev->home.hm2$l_altidxlbn), 512, address + (512 * ((size_t)idxlbn - phyblk)) )) ) return NULL; } wrtlen -= phylen; curvbn += phylen; address += (size_t)phylen * 512; } } while( length > 0 && modmask != 0 ); vioc->modmask = 0; vioc->cache.objmanager = NULL; } return cacheobj; } /************************************************************ deaccesschunk() */ /* deaccesschunk() to deaccess a VIOC (chunk of a file) */ vmscond_t deaccesschunk( struct VIOC *vioc, uint32_t wrtvbn, int wrtblks, int reuse ) { #if DEBUG printf( "Deaccess chunk %u (%08x) wrtvbn: %u blks: %u ru: %u\n", vioc->cache.hashval, vioc->cache.hashval, wrtvbn, wrtblks, reuse ); #endif if( wrtvbn ) { register unsigned modmask; if( wrtvbn <= vioc->cache.hashval || wrtvbn + wrtblks > vioc->cache.hashval + VIOC_CHUNKSIZE + 1 ) { #if DEBUG printf( "VBN out of reserved range\n" ); #endif return SS$_BADPARAM; } modmask = 1 << (wrtvbn - vioc->cache.hashval - 1); while( --wrtblks > 0 ) modmask |= modmask << 1; if( (vioc->wrtmask | modmask) != vioc->wrtmask ) { #if DEBUG printf( "Write mask error %08x %08x\n", (vioc->wrtmask | modmask), vioc->wrtmask ); #endif return SS$_WRITLCK; } vioc->modmask |= modmask; if( vioc->cache.refcount == 1 ) vioc->wrtmask = 0; vioc->cache.objmanager = vioc_manager; } cache_untouch( &vioc->cache, reuse ); return SS$_NORMAL; } /************************************************************** vioc_create() */ static void *vioc_create( uint32_t hashval, void *keyval, vmscond_t *retsts ) { register struct VIOC *vioc; register uint32_t length; register uint32_t curvbn; register char *address; register struct FCB *fcb; vioc = (struct VIOC *) calloc( 1, sizeof(struct VIOC) ); if( vioc == NULL ) { *retsts = SS$_INSFMEM; return NULL; } curvbn = hashval + 1; fcb = (struct FCB *) keyval; vioc->cache.objmanager = NULL; vioc->cache.objtype = OBJTYPE_VIOC; vioc->fcb = fcb; vioc->wrtmask = 0; vioc->modmask = 0; length = fcb->hiblock - curvbn + 1; if( length > VIOC_CHUNKSIZE ) length = VIOC_CHUNKSIZE; address = (char *) vioc->data; do { register vmscond_t sts; uint32_t phyblk = 0, phylen = 0; struct VCBDEV *vcbdev = NULL; if (fcb->highwater != 0 && curvbn >= fcb->highwater) { memset( address, 0, (size_t)length * 512 ); break; } if( $SUCCESSFUL(sts = getwindow( fcb, curvbn, &vcbdev, &phyblk, &phylen, NULL, NULL )) ) { if( phylen > length ) phylen = length; if( fcb->highwater != 0 && curvbn + phylen > fcb->highwater ) { phylen = fcb->highwater - curvbn; } sts = virt_read( vcbdev->dev, phyblk, phylen * 512, address ); } if( $FAILED(sts) ) { free(vioc); *retsts = sts; return NULL; } length -= phylen; curvbn += phylen; address += (size_t)phylen * 512; } while( length > 0 ); *retsts = SS$_NORMAL; return vioc; } /************************************************************** accesschunk() */ /* accesschunk() return pointer to a 'chunk' of a file ... */ vmscond_t accesschunk( struct FCB *fcb, uint32_t vbn, struct VIOC **retvioc, char **retbuff, uint32_t *retblocks, unsigned wrtblks ) { vmscond_t sts; register uint32_t blocks; register struct VIOC *vioc; #if DEBUG printf("Access chunk %u (%u)\n", vbn, fcb->cache.hashval); #endif if (vbn < 1 || vbn > fcb->hiblock) return SS$_ENDOFFILE; blocks = (vbn - 1) / VIOC_CHUNKSIZE * VIOC_CHUNKSIZE; if (wrtblks) { if( !(fcb->status & FCB_WRITE) ) return SS$_WRITLCK; if (vbn + wrtblks > blocks + VIOC_CHUNKSIZE + 1) { return SS$_BADPARAM; } } vioc = cache_find((void *) &fcb->vioc, blocks, fcb, &sts, NULL, vioc_create); if (vioc == NULL) return sts; /* Return result to caller... */ *retvioc = vioc; blocks = vbn - blocks - 1; *retbuff = vioc->data[blocks]; if (wrtblks || retblocks != NULL) { register unsigned modmask = 1 << blocks; blocks = VIOC_CHUNKSIZE - blocks; if (vbn + blocks > fcb->hiblock) blocks = fcb->hiblock - vbn + 1; if (wrtblks && blocks > wrtblks) blocks = wrtblks; if (retblocks != NULL) *retblocks = blocks; if (wrtblks && blocks) { if( fcb->highwater != 0 && vbn + blocks > fcb->highwater ) { fcb->highwater = vbn + blocks; fcb->head->fh2$l_highwater = F11LONG( fcb->highwater ); } while( --blocks > 0 ) modmask |= modmask << 1; vioc->wrtmask |= modmask; vioc->cache.objmanager = vioc_manager; } } return SS$_NORMAL; } /************************************************************* deaccessfile() */ /* deaccessfile() finish accessing a file.... */ vmscond_t deaccessfile( struct FCB *fcb ) { #if DEBUG printf("Deaccessing file (%u) reference %d\n", fcb->cache.hashval, fcb->cache.refcount); #endif if( fcb->cache.refcount == 1 ) { register unsigned refcount; refcount = cache_refcount((struct CACHE *) fcb->vioc); if( refcount && fcb->vioc == fcb->headvioc ) { /* Index file header and data may share a VIOC. * In this case, there's no way to know how * many of the refs are from each source. Assume 1 from header. */ --refcount; } refcount += cache_refcount((struct CACHE *) fcb->wcb); if( refcount != 0 ) { #if DEBUG printf("File reference counts non-zero %d (%d)\n", refcount, fcb->cache.hashval); #endif #if DEBUG printf("File reference counts non-zero %d %d\n", cache_refcount((struct CACHE *) fcb->wcb), cache_refcount((struct CACHE *) fcb->vioc)); #endif return SS$_BUGCHECK; } if( (fcb->status & FCB_WRITE ) && (fcb->head->fh2$l_filechar & F11LONG(FH2$M_MARKDEL)) ) { return deallocfile(fcb); } } cache_untouch(&fcb->cache, TRUE); return SS$_NORMAL; } /************************************************************** fcb_manager() */ /* Object manager for FCB objects:- we point to one of our sub-objects (vioc or wcb) in preference to letting the cache routines get us! But we when run out of excuses it is time to clean up the file header... :-( */ void *fcb_manager(struct CACHE *cacheobj, int flushonly) { register struct FCB *fcb; fcb = (struct FCB *) cacheobj; if( fcb->vioc != NULL ) return &fcb->vioc->cache; if( fcb->wcb != NULL ) return &fcb->wcb->cache; if( fcb->cache.refcount != 0 || flushonly) return NULL; if( fcb->headvioc != NULL ) { deaccesshead(fcb->headvioc, fcb->head, fcb->headvbn); fcb->headvioc = NULL; } return cacheobj; } /*************************************************************** fcb_create() */ static void *fcb_create( uint32_t filenum, void *keyval, vmscond_t *retsts ) { register struct FCB *fcb; fcb = (struct FCB *) calloc( 1, sizeof(struct FCB) ); UNUSED(filenum); UNUSED(keyval); if (fcb == NULL) { *retsts = SS$_INSFMEM; } else { fcb->cache.objmanager = fcb_manager; fcb->cache.objtype = OBJTYPE_FCB; fcb->hiblock = 100000; } return fcb; } /*************************************************************** accessfile() */ /* accessfile() open up file for access... */ vmscond_t accessfile( struct VCB * vcb, struct fiddef * fid, struct FCB **fcbadd, unsigned wrtflg ) { vmscond_t sts; register struct FCB *fcb; register uint32_t filenum; filenum = (fid->fid$b_nmx << 16) + fid->fid$w_num; #if DEBUG printf("Accessing file (%d,%d,%d) wrt %u\n", (fid->fid$b_nmx << 16) + fid->fid$w_num, fid->fid$w_seq, fid->fid$b_rvn, wrtflg); #endif if( filenum < 1 ) return SS$_BADPARAM; if( wrtflg && !(vcb->status & VCB_WRITE) ) return SS$_WRITLCK; if( fid->fid$b_rvn > 1 ) filenum |= fid->fid$b_rvn << 24; fcb = cache_find( (void *) &vcb->fcb, filenum, NULL, &sts, NULL, fcb_create ); if( fcb == NULL ) return sts; /* If not found make one... */ *fcbadd = fcb; if( fcb->vcb == NULL ) { fcb->rvn = fid->fid$b_rvn; if( fcb->rvn == 0 && vcb->devices > 1 ) fcb->rvn = 1; fcb->vcb = vcb; } if( wrtflg ) { if( fcb->headvioc != NULL && !(fcb->status & FCB_WRITE) ) { deaccesshead(fcb->headvioc, NULL, 0); fcb->headvioc = NULL; } fcb->status |= FCB_WRITE; } if( fcb->headvioc == NULL ) { register vmscond_t sts; if( $SUCCESSFUL(sts = accesshead(vcb, fid, 0, &fcb->headvioc, &fcb->head, &fcb->headvbn, wrtflg)) ) { fcb->hiblock = F11SWAP(fcb->head->fh2$w_recattr.fat$l_hiblk); if (fcb->head->fh2$b_idoffset > 39) { fcb->highwater = F11LONG(fcb->head->fh2$l_highwater); } else { fcb->highwater = 0; } } else { #if DEBUG printf("Accessfile fail status %08x\n", sts); #endif fcb->cache.objmanager = NULL; cache_untouch(&fcb->cache, TRUE); cache_delete(&fcb->cache); return sts; } } return SS$_NORMAL; } /***************************************************************** dismount() */ /* dismount() finish processing on a volume */ vmscond_t dismount (struct VCB * vcb, options_t options ) { register vmscond_t sts, device; int expectfiles; int openfiles; struct VCBDEV *vcbdev; expectfiles = vcb->devices; openfiles = cache_refcount( &vcb->fcb->cache ); if( vcb->status & VCB_WRITE ) expectfiles *= 2; #if DEBUG printf("Dismounting disk %d\n", openfiles); #endif sts = SS$_NORMAL; if( openfiles != expectfiles ) { printmsg( DISM_CANNOTDMT, 0, 12, ( (vcb->vcbdev[0].home.hm2$w_rvn? vcb->vcbdev[0].home.hm2$t_strucname: vcb->vcbdev[0].home.hm2$t_volname) ) ); return printmsg( DISM_USERFILES, MSG_CONTINUE, DISM_CANNOTDMT, openfiles - expectfiles ); } vcbdev = vcb->vcbdev; for( device = 0; device < vcb->devices; device++, vcbdev++ ) { #if DEBUG printf( "--->dismount(): vcbdev[%d] = \"%s\"\n", device, vcbdev->dev != NULL ? vcbdev->dev->devnam : "NULL" ); #endif if( vcbdev->dev != NULL ) { if( (vcb->status & VCB_WRITE) && vcbdev->mapfcb != NULL ) { sts = deaccessfile( vcbdev->mapfcb ); #if DEBUG printf( "--->dismount(): %s = deaccessfile(bitmap)\n", getmsg(sts, 0) ); #endif if( $FAILED(sts) ) { printmsg( DISM_CANNOTDMT, 0, 12, vcbdev->home.hm2$t_volname ); sts = printmsg( sts, MSG_CONTINUE, DISM_CANNOTDMT ); break; } vcbdev->mapfcb->status &= ~FCB_WRITE; vcbdev->mapfcb = NULL; } while (vcb->dircache) { cache_delete((struct CACHE *) vcb->dircache); #if DEBUG printf( "--->dismount(): cache_delete(dircache)\n" ); #endif } /* Remove all unreferenced fcbs - the LRU list still has references * on file headers. This reduces the references to just the index file itself. */ cache_remove( &vcb->fcb->cache ); #if DEBUG printf( "--->dismount(): cache_remove()\n" ); #endif /* Index file data & header. Special handling due to header also being * file data for the index file. */ deaccesshead( vcbdev->idxfcb->headvioc, vcbdev->idxfcb->head, vcbdev->idxfcb->headvbn ); /* Check status? */ vcbdev->idxfcb->headvioc = NULL; cache_untouch(&vcbdev->idxfcb->cache, FALSE); vcbdev->idxfcb->status &= ~FCB_WRITE; vcbdev->idxfcb = NULL; /* Remove index fcb, which should be the last in the FCB cache. */ cache_remove( &vcb->fcb->cache ); #if DEBUG printf( "--->dismount(): %s = deaccess(index)\n", getmsg(sts, 0) ); #endif if( $FAILED(sts) ) break; } } if( $SUCCESSFUL(sts) ) { struct VCB *lp; vcbdev = vcb->vcbdev; for (device = 0; device < vcb->devices; device++, vcbdev++) { if( vcbdev->dev != NULL ) { #if DEBUG printf( "--->dismount(): virt_close(%s)\n", vcbdev->dev->devnam ); #endif virt_close( vcbdev->dev ); vcbdev->dev = NULL; if( options & MOU_LOG ) sts = printmsg( (vcb->devices > 1)? DISM_DISMOUNTD: DISM_DISMOUNTDV, 0, 12, vcbdev->home.hm2$t_volname, 12, ( (vcb->vcbdev[0].home.hm2$w_rvn? vcb->vcbdev[0].home.hm2$t_strucname: vcb->vcbdev[0].home.hm2$t_volname) ) ); } } for( lp = (struct VCB *)&vcb_list; lp->next != NULL; lp = lp->next ) { if( lp->next == vcb ) { lp->next = vcb->next; break; } } if( vcb->devices > 1 ) sts = printmsg( DISM_DISMAL, 0, 12, vcb->vcbdev[0].home.hm2$t_strucname ); free(vcb); } return sts; } /******************************************************************** mount() */ #if DEBUG #ifndef HOME_SKIP #define HOME_SKIP 1 #endif #ifndef HOME_LIMIT #define HOME_LIMIT 3 #endif #else #ifndef HOME_SKIP #define HOME_SKIP 0 #endif #ifndef HOME_LIMIT #define HOME_LIMIT 1000 #endif #endif /* mount() make disk volume available for processing... */ vmscond_t mount( options_t flags, unsigned devices, char *devnam[], char *label[] ) { register unsigned device; vmscond_t sts = 0; struct VCB *vcb; struct VCBDEV *vcbdev; struct VOLSETREC *volsets = NULL; #if DEBUG if (sizeof(struct HOME) != 512 || sizeof(struct HEAD) != 512) return SS$_BUGCHECK; #endif /* calloc() initializes most of the structure */ vcb = (struct VCB *) calloc( 1, sizeof(struct VCB) + (((size_t)devices - 1) * sizeof(struct VCBDEV)) ); if( vcb == NULL ) return SS$_INSFMEM; if( flags & MOU_WRITE ) vcb->status |= VCB_WRITE; /* Pass 1: validate device name & open each for I/O. * Read HOM blocks & verify labels. */ vcbdev = vcb->vcbdev; for( device = 0; device < devices; device++, vcbdev++ ) { char *dname; uint32_t errcnt = 0; dname = devnam[device]; sts = MOUNT_FAILED; vcbdev->dev = NULL; if( *dname != '\0' ) { /* Really want to allow skipping volumes? */ unsigned int hba, delta, homtry; if( label[device] != NULL && strlen(label[device]) > sizeof( volsets[0].vsr$t_label ) ) { sts = printmsg( MOUNT_BADLABEL, 0, label[device] ); break; } if( $FAILS(sts = virt_open( &dname, flags, &vcbdev->dev )) ) break; vcb->devices++; if (vcbdev->dev->vcb != NULL) { sts = printmsg( SS$_DEVMOUNT, 0 ); break; } if( (flags & MOU_WRITE ) && !(vcbdev->dev->access & MOU_WRITE) ) { sts = printmsg( MOUNT_WRITELCK, 0, devnam[device] ); vcb->status &= ~VCB_WRITE; } delta = delta_from_index( (vcbdev->dev->access & MOU_DEVTYPE) >> MOU_V_DEVTYPE ); for( hba = 1, homtry = 0; homtry < HOME_LIMIT; homtry++, hba += delta ) { struct HOME *hom; #if HOME_SKIP > 100 if( homtry < HOME_SKIP ) continue; #endif if( $FAILS(sts = virt_read( vcbdev->dev, hba, sizeof( struct HOME ), (char *) &vcbdev->home )) ) break; hom = &vcbdev->home; #if DEBUG || defined( HOME_LOG ) printf( "--->mount(%u): LBA=%u, HM2$L_HOMELBN=%u, " "HM2$L_ALHOMELBN=%u, " "HM2$T_FORMAT=\"%12.12s\", memcmp()=%d,%d\n", homtry+1, hba, F11LONG( hom->hm2$l_homelbn ), F11LONG( hom->hm2$l_alhomelbn ), hom->hm2$t_format, memcmp( hom->hm2$t_format, "DECFILE11B ", 12 ), memcmp( hom->hm2$t_format, "FILES11B_L0 ", 12 ) ); #endif if( (hba == F11LONG(hom->hm2$l_homelbn)) && ((F11LONG(hom->hm2$l_alhomelbn) != 0) || (memcmp( hom->hm2$t_format, "FILES11B_L0 ", 12 ) == 0)) && (F11LONG(hom->hm2$l_altidxlbn) != 0) && (F11WORD(hom->hm2$w_homevbn) != 0) && (F11LONG(hom->hm2$l_ibmaplbn) != 0) && (F11WORD(hom->hm2$w_ibmapsize) != 0) && (F11WORD(hom->hm2$w_resfiles) >= 5) && (F11WORD(hom->hm2$w_checksum1) == checksumn( (f11word *)hom, (offsetof( struct HOME, hm2$w_checksum1 )/2) )) && (F11WORD(hom->hm2$w_checksum2) == checksum( (f11word *)hom )) && ((memcmp(hom->hm2$t_format, "DECFILE11B ", 12) == 0) || (memcmp( hom->hm2$t_format, "FILES11B_L0 ", 12 ) == 0)) ) { break; } errcnt++; sts = MOUNT_HOMBLKCHK; #if DEBUG || defined( HOME_LOG ) printf( "--->mount(): Home block validation failure\n" ); printf( "(F11LONG(hom->hm2$l_alhomelbn) != 0) %u\n", (F11LONG(hom->hm2$l_alhomelbn) != 0) ); printf( "(F11LONG(hom->hm2$l_altidxlbn) != 0) %u\n", (F11LONG(hom->hm2$l_altidxlbn) != 0) ); printf( "(F11WORD(hom->hm2$w_homevbn) != 0) %u\n", (F11WORD(hom->hm2$w_homevbn) != 0)); printf( "(F11LONG(hom->hm2$l_ibmaplbn) != 0) %u\n", (F11LONG(hom->hm2$l_ibmaplbn) != 0) ); printf( "(F11WORD(hom->hm2$w_ibmapsize) != 0) %u\n", (F11WORD(hom->hm2$w_ibmapsize) != 0)); printf( "(F11WORD(hom->hm2$w_resfiles) >= 5) %u\n", (F11WORD(hom->hm2$w_resfiles) >= 5)); printf( "(F11WORD(hom->hm2$w_checksum1) = %u %u\n", F11WORD(hom->hm2$w_checksum1), checksumn( (f11word *)hom, (offsetof( struct HOME, hm2$w_checksum1 )/2) ) ); printf( "(F11WORD(hom->hm2$w_checksum2) = %u %u\n", F11WORD(hom->hm2$w_checksum2), checksum( (f11word *)hom )); #endif } if( $FAILED(sts) ) { sts = printmsg( sts, 0, errcnt ); break; } if( (F11WORD(vcbdev->home.hm2$w_struclev) >> 8) != 2 || (F11WORD(vcbdev->home.hm2$w_struclev) & 0xFFu) < 1 ) { sts = printmsg( sts, 0, F11WORD(vcbdev->home.hm2$w_struclev) >> 8, F11WORD(vcbdev->home.hm2$w_struclev) & 0XFFu ); break; } if( errcnt && (flags & MOU_LOG) ) /* Suppress warning if /nolog for scripted mounts */ printmsg( $SETLEVEL(MOUNT_HOMBLKCHK, WARNING), 0, errcnt ); if( label[device] != NULL ) { int i; char lbl[12+1]; /* Pad CLI-supplied label to match ODS */ (void) snprintf( lbl, sizeof(lbl), "%-12s", label[device] ); for( i = 0; i < 12; i++ ) { if( toupper( lbl[i] ) != toupper( vcbdev->home.hm2$t_volname[i] ) ) { sts = printmsg( MOUNT_WRONGVOL, 0, dname, 12, vcbdev->home.hm2$t_volname, lbl ); break; } } if( $FAILED(sts) ) break; } if( (F11WORD(vcbdev->home.hm2$w_rvn) != device + 1) && !(F11WORD(vcbdev->home.hm2$w_rvn) == 0 && device == 0) ) { if( vcbdev->home.hm2$w_rvn == 0 ) { sts = printmsg( MOUNT_NOTVSMEM, 0, dname, 12, vcbdev->home.hm2$t_volname, device+1, 12, vcb->vcbdev[0].home.hm2$t_strucname ); } else { sts = printmsg( MOUNT_WRONGRVN, 0, dname, 12, vcbdev->home.hm2$t_volname, F11WORD(vcbdev->home.hm2$w_rvn), 12, vcbdev->home.hm2$t_strucname, device + 1, 12, vcb->vcbdev[0].home.hm2$t_strucname ); } } if( $FAILED(sts) ) break; } /* for(each device) */ } if( $FAILED(sts) ) { vcbdev = vcb->vcbdev; for( device = 0; device < vcb->devices; device++, vcbdev++ ) { if (vcbdev->dev == NULL) { continue; } virt_close( vcbdev->dev ); } free( vcb ); vcb = NULL; } else { /* Pass 2: Open the index file. * For RVN 1 of a volume set, read VOLSET.SYS. * For writable volumes, open and validate BITMAP.SYS. */ vcbdev = vcb->vcbdev; for( device = 0; device < devices; device++, vcbdev++ ) { char *dname; dname = devnam[device]; sts = MOUNT_FAILED; vcbdev->idxfcb = NULL; vcbdev->mapfcb = NULL; vcbdev->clustersize = 0; vcbdev->max_cluster = 0; vcbdev->free_clusters = 0; if( *dname != '\0' ) { struct fiddef idxfid = { FID$C_INDEXF, FID$C_INDEXF, 0, 0 }; idxfid.fid$b_rvn = device + 1; if( !(vcb->status & ~VCB_WRITE) ) vcbdev->dev->access &= ~MOU_WRITE; if( $FAILS(sts = accessfile( vcb, &idxfid, &vcbdev->idxfcb, (vcb->status & VCB_WRITE) != 0 )) ) { virt_close( vcbdev->dev ); vcbdev->dev = NULL; break; } vcbdev->dev->vcb = vcb; if( F11WORD(vcbdev->home.hm2$w_rvn) != 0 ) { if( device == 0 ) { struct fiddef vsfid = { FID$C_VOLSET, FID$C_VOLSET, 1, 0 } ; struct FCB *vsfcb = NULL; struct VIOC *vioc = NULL; unsigned rec; uint32_t vbn = 1; struct VOLSETREC *bufp = NULL; unsigned setcount; setcount = F11WORD(vcbdev->home.hm2$w_setcount); if( setcount != devices ) { sts = printmsg( MOUNT_WRONGVOLCNT, 0, 12, vcbdev->home.hm2$t_strucname, setcount, devices ); break; } /* Read VOLSET.SYS * 1 record for volset name + 1 for each member's label. */ volsets = (struct VOLSETREC *)malloc( (1+(size_t)setcount) * sizeof( struct VOLSETREC ) ); if( volsets == NULL ) { printmsg( MOUNT_VOLSETSYS, 0, dname ); sts = printmsg( SS$_INSFMEM, MSG_CONTINUE, MOUNT_VOLSETSYS ); break; } if( $FAILS(sts = accessfile( vcb, &vsfid, &vsfcb, 0 )) ) { printmsg( MOUNT_VOLSETSYS, 0, dname ); sts = printmsg( sts, MSG_CONTINUE, MOUNT_VOLSETSYS ); break; } for( rec = 0; rec <= setcount; ) { uint32_t blocks = 0, recs; if( $FAILS(sts = accesschunk( vsfcb, vbn, &vioc, (char **)&bufp, &blocks, 0 )) ) break; vbn += blocks; recs = (size_t)blocks * 512 / sizeof( struct VOLSETREC ); if( rec + recs > setcount + 1 ) recs = setcount + 1 - rec; memcpy(volsets+rec, bufp, recs * sizeof( struct VOLSETREC )); rec += recs; deaccesschunk( vioc, 0, 0, 0 ); } { vmscond_t st2; st2 = deaccessfile( vsfcb ); if( $SUCCESSFUL(sts) ) sts = st2; } if( $FAILED(sts) ) { printmsg( MOUNT_VOLSETSYS, 0, dname ); sts = printmsg( sts, MSG_CONTINUE, MOUNT_VOLSETSYS ); break; } if( memcmp(vcbdev->home.hm2$t_strucname, volsets[0].vsr$t_label, 12 ) != 0 ) { sts = printmsg( MOUNT_WRONGVOLMEM, 0, dname, 12, vcbdev->home.hm2$t_strucname, 12, volsets[0].vsr$t_label ); break; } } else { /* device != 0 */ if( vcb->vcbdev[0].dev == NULL ) { sts = printmsg( MOUNT_RVN1NOTMNT, 0 ); break; } if( memcmp(vcbdev->home.hm2$t_strucname, vcb->vcbdev[0].home.hm2$t_strucname, 12) != 0 ) { sts = printmsg( MOUNT_INCONVOL, 0, 12, vcbdev->home.hm2$t_volname, dname, 12, vcbdev->home.hm2$t_strucname, 12, vcb->vcbdev[0].home.hm2$t_strucname ); break; } } /* All volset members */ if( memcmp(vcbdev->home.hm2$t_volname, volsets[device+1].vsr$t_label, 12 ) != 0 ) { sts = printmsg( MOUNT_VOLOOO, 0, device+1, 12, vcb->vcbdev[0].home.hm2$t_strucname, 12, volsets[device+1].vsr$t_label, dname, 12, vcbdev->home.hm2$t_volname ); break; } } /* rvn != 0 */ /* All volumes */ if( vcb->status & VCB_WRITE ) { #if DEBUG struct SCB scb; if( $FAILS(sts = get_scb( vcb, device, &scb )) ) break; printf( "%d of %d blocks are free on %12.12s\n", vcbdev->free_clusters * vcbdev->clustersize, scb.scb$l_volsize, vcbdev->home.hm2$t_volname ); #else if( $FAILS(sts = get_scb( vcb, device, NULL )) ) break; #endif } if( $SUCCESSFUL(sts) && (flags & MOU_LOG) ) { sts = printmsg( MOUNT_MOUNTED, 0, 12, vcbdev->home.hm2$t_volname, vcbdev->dev->devnam ); } } /* device len */ } /* for( each device ) */ if( $FAILED(sts) ) { /* Dismount any volumes prior to the error */ vcbdev = vcb->vcbdev; for( device = 0; device < devices; device++, vcbdev++ ) { if (vcbdev->dev == NULL) { continue; } if( vcb->status & VCB_WRITE && vcbdev->mapfcb != NULL ) { /* check status? */ deaccessfile(vcbdev->mapfcb); vcbdev->idxfcb->status &= ~FCB_WRITE; vcbdev->mapfcb = NULL; } while( vcb->dircache ) cache_delete( (struct CACHE *) vcb->dircache ); cache_remove( &vcb->fcb->cache ); if( vcbdev->idxfcb != NULL ) { /* check status? = */ deaccesshead( vcbdev->idxfcb->headvioc, vcbdev->idxfcb->head, vcbdev->idxfcb->headvbn ); vcbdev->idxfcb->headvioc = NULL; cache_untouch( &vcbdev->idxfcb->cache, FALSE ); vcbdev->idxfcb->status &= ~FCB_WRITE; vcbdev->idxfcb = NULL; } vcbdev->dev->vcb = NULL; virt_close( vcbdev->dev ); } cache_remove( &vcb->fcb->cache ); } } /* Wrap up */ if( $SUCCESSFUL(sts) && (flags & MOU_LOG) && F11WORD(vcb->vcbdev[0].home.hm2$w_rvn) != 0 ) { sts = printmsg ( MOUNT_VSMOUNTED, 0, 12, vcb->vcbdev[0].home.hm2$t_strucname ); } if( vcb != NULL ) { struct VCB *lp; if( $SUCCESSFUL(sts) ) { for( lp = (struct VCB *)&vcb_list; lp->next != NULL; lp = lp->next ) ; lp->next = vcb; vcb->next = NULL; } else { free( vcb ); vcb = NULL; } } if( volsets != NULL ) free( volsets ); if( $SUCCESSFUL(sts) ) (void) mountdef( vcb->vcbdev[0].dev->devnam ); return sts; } /***************************************************************** get_scb() */ static vmscond_t get_scb( struct VCB *vcb, unsigned device, struct SCB *retscb ) { vmscond_t sts; int mapopen = 1; struct fiddef mapfid = { FID$C_BITMAP, FID$C_BITMAP, 0, 0 }; struct VCBDEV *vcbdev = NULL; struct VIOC *vioc = NULL; struct SCB *scb = NULL; int volwrt; if( retscb != NULL ) memset( retscb, 0, sizeof( struct SCB ) ); vcbdev = vcb->vcbdev + device; mapfid.fid$b_rvn = (f11byte) F11WORD(vcbdev->home.hm2$w_rvn); volwrt = (vcb->status & VCB_WRITE) != 0; if( vcbdev->mapfcb == NULL ) { mapopen = 0; if( $FAILS(sts = accessfile( vcb, &mapfid, &vcbdev->mapfcb, volwrt )) ) { vcb->status &= ~VCB_WRITE; vcbdev->mapfcb = NULL; printmsg( MOUNT_BITMAPSYS, 0, vcbdev->dev->devnam ); return printmsg( sts, MSG_CONTINUE, MOUNT_BITMAPSYS ); } } if( $FAILS(sts = accesschunk( vcbdev->mapfcb, 1, &vioc, (char **) &scb, NULL, 0 )) ) { if( !mapopen ) { deaccessfile( vcbdev->mapfcb ); vcbdev->mapfcb = NULL; vcb->status &= ~VCB_WRITE; } printmsg( MOUNT_BITMAPSYS, 0, vcbdev->dev->devnam ); return printmsg( sts, MSG_CONTINUE, MOUNT_BITMAPSYS ); } if( retscb != NULL ) memcpy( retscb, scb, sizeof( struct SCB ) ); if( (F11WORD(scb->scb$w_checksum) != checksum( (f11word *)scb )) || (scb->scb$w_cluster != vcbdev->home.hm2$w_cluster) ) { deaccesschunk(vioc, 0, 0, FALSE); if( !mapopen ) { deaccessfile( vcbdev->mapfcb ); vcbdev->mapfcb = NULL; vcb->status &= ~VCB_WRITE; } printmsg( MOUNT_BITMAPSYS, 0, vcbdev->dev->devnam ); return printmsg( MOUNT_BADSCB, MSG_CONTINUE, MOUNT_BITMAPSYS ); } vcbdev->clustersize = F11WORD( vcbdev->home.hm2$w_cluster ); vcbdev->max_cluster = ( (F11LONG(scb->scb$l_volsize) + F11WORD(scb->scb$w_cluster) - 1) / F11WORD(scb->scb$w_cluster) ) -1; deaccesschunk(vioc, 0, 0, FALSE); if( !volwrt || !mapopen ) { if( $FAILS(sts = update_freecount( vcbdev, &vcbdev->free_clusters )) ) { printmsg( MOUNT_BITUPDFAIL, 0, vcbdev->dev->devnam ); sts = printmsg( sts, MSG_CONTINUE, MOUNT_BITUPDFAIL ); } if( !(mapopen || volwrt) ) { deaccessfile( vcbdev->mapfcb ); vcbdev->mapfcb = NULL; } } return sts; } /***************************************************************** show_volumes */ /* First, some private helper routines */ /********************************************************** print_volhdr */ static void print_volhdr( int volset, size_t devwid ) { size_t i; if( volset ) printf( " RVN " ); else printf( " " ); printf( "%-*s Volume/User Lvl Clust Owner/CreateDate VolProt/Default FileProt\n", (int)devwid, "Dev" ); if( volset ) printf( " --- " ); else printf( " " ); for( i= 0; i < devwid; i++ ) putchar( '-' ); printf( " ------------ --- ----- ----------------- ---------------------------\n" ); return; } /********************************************************** print_prot() */ static void print_prot( f11word code, int vol ) { static const char grp[4] = { "SOGW" }; static const char acc[2][4] = {{"RWED"}, {"RWCD"}}; int g, b, w; w = 27; for( g = 0; g < 4; g++ ) { w -= printf( "%c:", grp[g] ); for( b = 0; b < 4; b++ ) { if( !(code & (1<<(b + (4*g)))) ) { putchar( acc[vol][b] ); w--; } } if( g < 3 ) { putchar( ',' ); w--; } } while( w-- ) putchar( ' ' ); } /********************************************************** print_volinf() */ static void print_volinf( struct VCB *vcb, size_t devwid, unsigned device, size_t wrap ) { struct VCBDEV *vcbdev; struct SCB scb; vmscond_t sts; size_t n; f11word timbuf[7]; char uicbuf[ 1+6+1+6+1+1], *p; vcbdev = vcb->vcbdev+device; if( $FAILS(sts = get_scb( vcb, device, &scb )) ) { if( $MATCHCOND(sts, SS$_BADPARAM) ) printf(" SCB is invalid\n" ); else printf( " *Unable to read SCB: %s\n", getmsg( sts, MSG_TEXT ) ); } for( n = 0; n < strlen( vcbdev->dev->devnam ); n++ ) { if( vcbdev->dev->devnam[n] == ':' ) break; putchar( vcbdev->dev->devnam[n] ); } printf( "%*s ", (int)(devwid-n), "" ); printf( "%12.12s", vcbdev->home.hm2$t_volname ); (void) snprintf(uicbuf, sizeof(uicbuf), "[%6o,%6o]", F11WORD( vcbdev->home.hm2$w_volowner.uic$w_grp ), F11WORD( vcbdev->home.hm2$w_volowner.uic$w_mem )); for( p = uicbuf; p[1] == ' '; p++ ) { p[0] = ' '; p[1] = '['; } p = strchr( uicbuf, ',' ); n = strspn( ++p, " " ); memmove( p, p + n, 7 - n ); memset( p + 7 - n, ' ', n ); printf( " %u.%u %5u %s", F11WORD( vcbdev->home.hm2$w_struclev ) >> 8, F11WORD( vcbdev->home.hm2$w_struclev ) & 0xFF, F11WORD( vcbdev->home.hm2$w_cluster ), uicbuf ); printf( " " ); print_prot( F11WORD( vcbdev->home.hm2$w_protect ), 1 ); if( !(vcb->status & VCB_WRITE) ) printf( " write-locked" ); if( $SUCCESSFUL(sts) ) { disktypep_t dp; int first = 1; for( dp = disktype; dp->name != NULL; dp++ ) { unsigned size; unsigned blksize = 1; size = dp->sectors * dp->tracks * dp->cylinders - dp->reserved; if( dp->sectorsize > 512 ) size = size * dp->sectorsize / 512; else { if( dp->sectorsize < 512 ) { blksize = (512 / dp->sectorsize); size = size / blksize; } } if( scb.scb$l_volsize == size && scb.scb$l_blksize == blksize && scb.scb$l_sectors == dp->sectors && scb.scb$l_tracks == dp->tracks && scb.scb$l_cylinders == dp->cylinders ) { if( first ) { printf( " type: " ); first = 0; } else printf( "|" ); printf( "%s", dp->name ); } } if( first ) printf( " Unrecognized type" ); } printf( "\n%*s %12.12s ", (int)(wrap+devwid), "", vcbdev->home.hm2$t_ownername ); if( $SUCCESSFUL(sys_numtim( timbuf, vcbdev->home.hm2$q_credate )) ) { static const char *months = "-JAN-FEB-MAR-APR-MAY-JUN-JUL-AUG-SEP-OCT-NOV-DEC-"; printf( "%2u%.5s%4u %2u:%2u ", timbuf[2], months+(4*((size_t)timbuf[1]-1)), timbuf[0], timbuf[3], timbuf[4]); } else printf( "%*s", 41-23, "" ); print_prot( F11WORD( vcbdev->home.hm2$w_fileprot ), 0 ); if( $SUCCESSFUL(sts) ) { printf( " Free: %u/%u", vcbdev->free_clusters * vcbdev->clustersize, scb.scb$l_volsize ); printf( " Geo: %u/%u/%u", F11LONG(scb.scb$l_sectors), F11LONG(scb.scb$l_tracks), F11LONG(scb.scb$l_cylinders) ); if( F11LONG(scb.scb$l_blksize) != 1 ) printf( " %u phys blks/LBN", F11LONG(scb.scb$l_blksize) ); } putchar( '\n' ); } /********************************************************** show_volumes() */ void show_volumes( void ) { struct VCB *vcb; size_t maxd = sizeof( "Dev" ) -1; int nvol = 0; unsigned device; if( vcb_list == NULL ) { printf( " No volumes mounted\n" ); return; } for( vcb = vcb_list; vcb != NULL; vcb = vcb->next ) { struct VCBDEV *vcbdev; if( vcb->devices == 0 ) { printf( "No devices for volume\n" ); abort(); } nvol++; vcbdev = vcb->vcbdev; for( device = 0; device < vcb->devices; device++, vcbdev++ ) { size_t n; for( n = 0; n < strlen( vcbdev->dev->devnam ); n++ ) if( vcbdev->dev->devnam[n] == ':' ) break; if( n > maxd ) maxd = n; } } for( vcb = vcb_list; vcb != NULL; vcb = vcb->next ) { struct VCBDEV *vcbdev; unsigned device; vcbdev = vcb->vcbdev; if( F11WORD(vcbdev->home.hm2$w_rvn) == 0 ) continue; nvol--; printf( " Volume set %12.12s\n", vcbdev->home.hm2$t_strucname ); print_volhdr( TRUE, maxd ); for( device = 0; device < vcb->devices; device++, vcbdev++ ) { printf( " %3d ", F11WORD(vcbdev->home.hm2$w_rvn) ); print_volinf( vcb, maxd, device, 8 ); } } if( nvol == 0 ) return; printf( "\n" ); print_volhdr( FALSE, maxd ); for( vcb = vcb_list; vcb != NULL; vcb = vcb->next ) { struct VCBDEV *vcbdev; unsigned device; vcbdev = vcb->vcbdev; if( F11WORD(vcbdev->home.hm2$w_rvn) != 0 ) continue; vcbdev = vcb->vcbdev; for( device = 0; device < vcb->devices; device++, vcbdev++ ) { printf( " " ); print_volinf( vcb, maxd, device, 2 ); } } return; } /*************************************************************** acccess_rundown() */ void access_rundown( void ) { struct VCB *vcb, *next = NULL; vmscond_t sts; for( vcb = vcb_list; vcb != NULL; vcb = next ) { next = vcb->next; if( $FAILS(sts = dismount( vcb, 0 )) ) { printf( "Dismount failed in rundown: %s\n", getmsg(sts, MSG_TEXT) ); } } }