Timothe Litt 66e00b9900 Backlog of work since 2016
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.
2022-10-10 11:00:20 -04:00

1725 lines
60 KiB
C

/* 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 <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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) );
}
}
}