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

1155 lines
40 KiB
C

/* Update.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.
*/
#if !defined( DEBUG ) && defined( DEBUG_UPDATE )
#define DEBUG DEBUG_UPDATE
#else
#ifndef DEBUG
#define DEBUG 0
#endif
#endif
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "access.h"
#include "device.h"
#include "initvol.h"
#include "ods2.h"
#include "phyio.h"
#include "ssdef.h"
#include "stsdef.h"
#ifdef VMS
#include <starlet.h>
#else
#include "vmstime.h"
#endif
/* Bitmaps are bitstrings stored little-endian; the LSB represents the first item
* (0 or 1) in the map. For storage allocation, a set bit indicates a free block.
* For file header allocation, a set bit indicates in-use.
*
* Bitmaps are accessed in 'WORK_UNITs' which can be a wide integer
* on a little endian machine but is always a byte on a big endian system.
* (The cost of byte-swapping the bitmaps exceeds the benefit of larger
* work units.)
*
* There is an implicit assumption that WORK_UNITs will not span a disk block
* (512 byte) boundary.
*/
#ifndef WORK_UNIT
#if ODS2_BIG_ENDIAN
#define WORK_UNIT uint8_t
#define WORK_MASK UINT8_MAX
#define WORK_C(n) UINT8_C(n)
#define WORK_BITS 8
#elif defined( INT64_MAX )
#define WORK_UNIT uint64_t
#define WORK_MASK UINT64_MAX
#define WORK_C(n) UINT64_C(n)
#define WORK_BITS 64
#else /* uint32_t is required for many other structures; no point going smaller */
#define WORK_UNIT uint32_t
#define WORK_MASK UINT32_MAX
#define WORK_C(n) UINT32_C(n)
#define WORK_BITS 32
#endif
#endif
/********************************************************* update_freecount() */
/* Read a device's storage allocation bitmap and tally the unused clusters.
*/
vmscond_t update_freecount( struct VCBDEV *vcbdev, uint32_t *retcount ) {
register vmscond_t sts = SS$_NORMAL;
register uint32_t free_clusters = 0;
register uint32_t map_block, map_end;
*retcount = 0;
map_end = (((size_t)vcbdev->max_cluster + 4095) / 4096) + 2; /* 1 VBN past bitmap end */
for( map_block = 2; map_block < map_end; ) { /* VBN 1 is SCB, 2 starts bitmap */
struct VIOC *vioc;
uint32_t blkcount = 0;
WORK_UNIT *bitmap = NULL, *work_ptr = NULL;
register uint32_t work_count = 0;
if( $FAILS(sts = accesschunk( vcbdev->mapfcb, map_block, &vioc,
(char **) &bitmap, &blkcount, 0 )) )
return sts;
if( blkcount > map_end - map_block )
blkcount = map_end - map_block + 1;
work_ptr = bitmap;
work_count = ((size_t)blkcount * 512) / sizeof(WORK_UNIT);
do {
register WORK_UNIT work_val;
work_val = *work_ptr++;
if( work_val == WORK_MASK ) {
free_clusters += WORK_BITS;
continue;
}
if( work_val == 0 )
continue;
#if WORK_BITS == 64
work_val = work_val - ((work_val >> 1) & UINT64_C(0x5555555555555555));
work_val = (work_val & UINT64_C(0x3333333333333333)) +
((work_val >> 2) & UINT64_C(0x3333333333333333));
free_clusters += (((work_val +
(work_val >> 4)) & UINT64_C(0x0f0f0f0f0f0f0f0f)) *
UINT64_C(0x0101010101010101)) >> 56;
#elif WORK_BITS == 32
work_val = work_val - ((work_val >> 1) & UINT32_C(0x55555555));
work_val = (work_val & UINT32_C(0x33333333)) +
((work_val >> 2) & UINT32_C(0x33333333));
free_clusters += (((work_val +
(work_val >> 4)) & UINT32_C(0x0f0f0f0f)) *
UINT32_C(0x01010101)) >> 24;
#elif WORK_BITS == 8
work_val = work_val - ((work_val >> 1) & UINT8_C(0x55));
work_val = (work_val & UINT8_C(0x33)) + ((work_val >> 2) & UINT8_C(0x33));
free_clusters += ((work_val + (work_val >> 4)) & UINT8_C(0x0f));
#else
while( work_val != 0 ) {
if( work_val & WORK_C(1) )
free_clusters++;
work_val = work_val >> 1;
}
#endif
} while( --work_count > 0 );
if( $FAILS(sts = deaccesschunk( vioc, 0, 0, TRUE)) )
return sts;
map_block += blkcount;
}
*retcount = free_clusters;
return sts;
}
/************************************************************ bitmap_modify() */
/* Set or clear a contiguous block of bits in the device storage allocation bitmap
*/
static vmscond_t bitmap_modify( struct VCBDEV *vcbdev,
uint32_t cluster, uint32_t count,
uint32_t release_flag ) {
register vmscond_t sts;
register uint32_t clust_count;
register uint32_t map_block;
register uint32_t block_offset;
clust_count = count;
map_block = (cluster / 4096) + 2; /* VBN 1 = SCB, 2 = bitmap start */
block_offset = cluster % 4096;
if( clust_count < 1 )
return SS$_BADPARAM;
if (cluster + clust_count > vcbdev->max_cluster + 1)
return SS$_BADPARAM;
if( release_flag )
vcbdev->free_clusters += clust_count;
else
vcbdev->free_clusters -= clust_count;
do {
struct VIOC *vioc = NULL;
uint32_t blkcount = 0;
WORK_UNIT *bitmap = NULL;
register WORK_UNIT *work_ptr = NULL;
register uint32_t work_count = 0;
register uint32_t bit_no = 0;
if( $FAILS(sts = accesschunk( vcbdev->mapfcb, map_block , &vioc,
(char **) &bitmap, &blkcount, 1 )) )
return sts;
work_ptr = bitmap + block_offset / WORK_BITS;
if( (bit_no = block_offset % WORK_BITS) != 0 ) {
register WORK_UNIT bit_mask;
bit_mask = WORK_MASK;
if( bit_no + clust_count < WORK_BITS ) {
bit_mask = bit_mask >> (WORK_BITS - clust_count);
clust_count = 0;
} else {
clust_count -= WORK_BITS - bit_no;
}
bit_mask = bit_mask << bit_no;
if (release_flag) {
*work_ptr |= bit_mask;
} else {
*work_ptr &= ~bit_mask;
}
++work_ptr;
block_offset += WORK_BITS - bit_no;
}
work_count = (blkcount * 4096 - block_offset) / WORK_BITS;
if( work_count > clust_count / WORK_BITS ) {
work_count = clust_count / WORK_BITS;
block_offset = 1;
} else {
block_offset = 0;
}
clust_count -= work_count * WORK_BITS;
if( release_flag ) {
while( work_count-- > 0 ) {
*work_ptr++ = WORK_MASK;
}
} else {
while( work_count-- > 0 ) {
*work_ptr++ = 0;
}
}
if( clust_count != 0 && block_offset ) {
register WORK_UNIT bit_mask;
bit_mask = WORK_MASK >> (WORK_BITS - clust_count);
if( release_flag ) {
*work_ptr |= bit_mask;
} else {
*work_ptr &= ~bit_mask;
}
++work_ptr;
clust_count = 0;
}
if( $FAILS(sts = deaccesschunk( vioc, map_block, blkcount, TRUE )) )
return sts;
map_block += blkcount;
block_offset = 0;
} while( clust_count != 0 );
return sts;
}
/************************************************************ bitmap_search() */
/* Find a pool of free clusters in the device's storage allocation bitmap */
static vmscond_t bitmap_search( struct VCBDEV *vcbdev,
uint32_t *position, uint32_t *count ) {
register vmscond_t sts;
register uint32_t map_block, block_offset;
register uint32_t search_words, needed;
register uint32_t run = 0, cluster;
register uint32_t best_run = 0, best_cluster = 0;
needed = *count;
if( needed < 1 )
return SS$_BADPARAM;
if( needed > (vcbdev->max_cluster + 1) * vcbdev->clustersize)
return SS$_DEVICEFULL;
needed = (needed + vcbdev->clustersize -1) / vcbdev->clustersize;
cluster = *position / vcbdev->clustersize;
if( cluster + needed > vcbdev->max_cluster + 1 )
cluster = 0;
map_block = (cluster / 4096) + 2;
block_offset = cluster % 4096;
cluster = cluster - (cluster % WORK_BITS);
search_words = (vcbdev->max_cluster +1 -cluster + (WORK_BITS -1)) / WORK_BITS;
do {
struct VIOC *vioc = NULL;
uint32_t blkcount = 0;
WORK_UNIT *bitmap = NULL;
register WORK_UNIT *work_ptr = NULL, work_val = 0;
register uint32_t work_count = 0;
if( $FAILS(sts = accesschunk( vcbdev->mapfcb, map_block, &vioc,
(char **) &bitmap, &blkcount, 0 )) )
return sts;
work_ptr = bitmap + (block_offset / WORK_BITS);
work_val = *work_ptr++;
if (block_offset % WORK_BITS) {
work_val = work_val & (WORK_MASK << (block_offset % WORK_BITS));
}
work_count = (((blkcount * 4096) + WORK_BITS -1) - block_offset) / WORK_BITS;
if (work_count > search_words)
work_count = search_words;
search_words -= work_count;
do {
if( work_val == WORK_MASK ) {
run += WORK_BITS;
if( run >= needed ) {
best_run = run;
best_cluster = cluster + WORK_BITS - run;
break;
}
} else {
register uint32_t bit_no = 0;
while( work_val != 0 ) {
if( work_val & 1 ) {
run++;
if( run >= needed ) {
best_run = run;
best_cluster = cluster + bit_no + 1 - run;
bit_no = WORK_BITS + 1;
break;
}
} else {
if( run >= needed || run > best_run ) {
best_run = run;
best_cluster = cluster + bit_no - run;
if( run >= needed ) {
bit_no = WORK_BITS + 1;
break;
}
}
run = 0;
}
work_val = work_val >> 1;
bit_no++;
}
if( bit_no < WORK_BITS ) {
if( run >= needed || run > best_run ) {
best_run = run;
best_cluster = cluster + bit_no - run;
if( run >= needed )
break;
}
run = 0;
}
}
cluster += WORK_BITS;
if( work_count-- <= 0 )
break;
work_val = *work_ptr++;
} while( best_run < needed );
if( $FAILS(sts = deaccesschunk( vioc, 0, 0, TRUE )) )
break;
if( best_run >= needed )
break;
if( search_words == 0 ) {
if( run > best_run ) {
best_run = run;
best_cluster = cluster - run;
}
if( *position == 0 )
break;
run = 0;
search_words = ( (((*position + vcbdev->clustersize -1)
/ vcbdev->clustersize) + (WORK_BITS -1))
/ WORK_BITS );
*position = 0;
cluster = 0;
map_block = 2;
} else
map_block += blkcount;
block_offset = 0;
} while( TRUE );
if( best_run > needed )
best_run = needed;
*count = best_run * vcbdev->clustersize;
*position = best_cluster * vcbdev->clustersize;
return sts;
}
#if 0
/************************************************************ headmap_clear() */
/* Release a header from the indexf.sys file header bitmap */
static vmscond_t headmap_clear( struct VCBDEV *vcbdev, uint32_t head_no ) {
WORK_UNIT *bitmap;
struct VIOC *vioc;
register vmscond_t sts;
register uint32_t map_block;
map_block = head_no / 4096 + vcbdev->home.hm2$w_cluster * 4 + 1;
if (head_no <= F11WORD(vcbdev->home.hm2$w_resfiles))
return SS$_NOPRIV; /* Protect reserved files */
if( $SUCCESSFUL(sts = accesschunk( vcbdev->idxfcb, map_block,
&vioc, (char **) &bitmap, NULL, 1)) ) {
bitmap[(head_no % 4096) / WORK_BITS] &= ~(WORK_C(1) << (head_no % WORK_BITS));
sts = deaccesschunk( vioc, map_block, 1, TRUE );
}
return sts;
}
#endif
/********************************************************** update_findhead() */
/* update_findhead() will locate or create a free header in indexf.sys
* The header will be marked allocated. indexf.sys is extended if necessary.
*/
static vmscond_t update_findhead( struct VCBDEV *vcbdev, uint32_t *rethead_no,
struct VIOC **retvioc, struct HEAD **headbuff,
uint32_t *retidxblk) {
uint32_t head_no = 0;
register vmscond_t sts = 0;
struct HEAD *idxhead = NULL;
uint32_t eofblk = 0;
head_no = 0;
idxhead = vcbdev->idxfcb->head;
eofblk = F11SWAP(idxhead->fh2$w_recattr.fat$l_efblk);
if( F11WORD(idxhead->fh2$w_recattr.fat$w_ffbyte) == 0 )
--eofblk;
sts = SS$_NORMAL;
do {
struct VIOC *vioc = NULL;
int modify_flag = FALSE;
uint32_t blkcount = 0;
WORK_UNIT *bitmap = NULL, *work_ptr = NULL;
register uint32_t map_block = 0, work_count = 0;
map_block = head_no / 4096 + F11WORD( vcbdev->home.hm2$w_cluster ) * 4 + 1;
if( $FAILS(sts = accesschunk( vcbdev->idxfcb, map_block,
&vioc, (char **)&bitmap, &blkcount, 1 )) )
return sts;
work_count = (head_no % 4096) / WORK_BITS;
work_ptr = bitmap + work_count;
work_count = blkcount * 512 / WORK_BITS - work_count;
do {
register WORK_UNIT work_val;
register uint32_t bit_no;
work_val = *work_ptr++;
if( work_val == WORK_MASK ) {
head_no += WORK_BITS;
continue;
}
bit_no = 0;
for( bit_no = 0;
bit_no < WORK_BITS;
bit_no++, head_no++ ) {
if( head_no < F11WORD(vcbdev->home.hm2$w_resfiles) )
continue;
if( !(work_val & (WORK_C(1) << bit_no)) ) {
register uint32_t idxblk = head_no +
F11WORD( vcbdev->home.hm2$w_ibmapvbn ) +
F11WORD( vcbdev->home.hm2$w_ibmapsize );
if( idxblk > eofblk ) {
uint32_t neweof;
neweof = idxblk + 1;
if( idxblk > vcbdev->idxfcb->hiblock ) {
f11long alq;
alq = F11WORD(vcbdev->idxfcb->head->fh2$w_recattr.fat$w_defext);
if( alq == 0 )
alq = F11WORD(vcbdev->home.hm2$w_extend);
if( alq == 0 )
alq = 1;
if( alq < idxblk - vcbdev->idxfcb->hiblock )
alq = idxblk - vcbdev->idxfcb->hiblock;
if( $FAILS(sts = update_extend( vcbdev->idxfcb, alq, 0 )) ) {
if( $FAILS( deaccesschunk( vioc, map_block, blkcount,
modify_flag )) )
abort();
return sts;
}
}
idxhead->fh2$w_recattr.fat$l_efblk = F11SWAP( neweof );
idxhead->fh2$w_recattr.fat$w_ffbyte = 0;
if( vcbdev->idxfcb->highwater != 0 && neweof > vcbdev->idxfcb->highwater ) {
vcbdev->idxfcb->highwater = neweof;
idxhead->fh2$l_highwater = F11LONG( neweof );
}
do {
if( $FAILS(sts = accesschunk( vcbdev->idxfcb, ++eofblk, retvioc,
(char **) headbuff, NULL, 1 )) ) {
deaccesschunk( vioc, map_block, blkcount, modify_flag );
return sts;
}
memset( *headbuff, 0, 512 );
if( eofblk != idxblk ) {
if( $FAILS(sts = deaccesschunk( *retvioc, eofblk, 1, 1 )) ) {
deaccesschunk( vioc, map_block, blkcount, modify_flag );
return sts;
}
}
} while( eofblk < idxblk );
work_ptr[-1] |= WORK_C( 1 ) << bit_no;
*rethead_no = head_no + 1;
*retidxblk = idxblk;
deaccesschunk( vioc, map_block, blkcount, TRUE );
return sts;
}
if( $SUCCESSFUL(sts = accesschunk( vcbdev->idxfcb, idxblk, retvioc,
(char **) headbuff, NULL, 1 )) ) {
if( (*headbuff)->fh2$w_checksum == 0 &&
(*headbuff)->fh2$w_fid.fid$w_num == 0 &&
(*headbuff)->fh2$w_fid.fid$b_nmx == 0 &&
(*headbuff)->fh2$w_fid.fid$b_rvn == 0 &&
((*headbuff)->fh2$l_filechar & F11LONG(FH2$M_MARKDEL)) ) {
work_ptr[-1] |= WORK_C( 1 ) << bit_no;
modify_flag = TRUE;
*rethead_no = head_no + 1;
*retidxblk = idxblk;
return deaccesschunk( vioc, map_block, blkcount, TRUE );
}
sts = deaccesschunk( *retvioc, 0, 0, FALSE );
}
}
}
} while( --work_count != 0 );
{
vmscond_t sts2;
if( $SUCCESSFUL(sts2 = deaccesschunk( vioc, map_block, blkcount, modify_flag )) )
sts = sts2;
}
if( $FAILED(sts) )
break;
} while( head_no < F11LONG(vcbdev->home.hm2$l_maxfiles) );
if( $SUCCESSFUL(sts) && head_no > F11LONG(vcbdev->home.hm2$l_maxfiles) )
return SS$_IDXFILEFULL;
return sts;
}
/*********************************************************** update_addhead() */
static vmscond_t update_addhead( struct VCB *vcb, char *filename,
struct fiddef *back,
uint32_t seg_num, struct fiddef *fid,
struct VIOC **vioc, struct HEAD **rethead,
uint32_t *idxblk ) {
register uint32_t free_space = 0;
vmscond_t sts;
register uint32_t device, rvn = 0;
size_t len;
uint32_t head_no = 0;
struct IDENT *id;
struct HEAD *head;
struct VCBDEV *vcbdev = NULL;
if( rethead != NULL )
*rethead = NULL;
for (device = 0; device < vcb->devices; device++) {
if (vcb->vcbdev[device].dev != NULL) {
if (vcb->vcbdev[device].free_clusters > free_space) {
free_space = vcb->vcbdev[device].free_clusters;
vcbdev = &vcb->vcbdev[device];
rvn = device;
}
}
}
if( vcbdev == NULL )
return SS$_DEVICEFULL;
if( $FAILS(sts = update_findhead( vcbdev, &head_no, vioc, &head, idxblk )) )
return sts;
#if DEBUG
printf("Header %d index %u rvn %u\n", head_no, *idxblk, rvn);
#endif
fid->fid$w_num = head_no;
fid->fid$w_seq = F11WORD(head->fh2$w_fid.fid$w_seq) + 1;
if( fid->fid$w_seq == 0 )
fid->fid$w_seq = 1;
if( rvn > 0 ) {
fid->fid$b_rvn = rvn + 1;
} else {
fid->fid$b_rvn = 0;
}
fid->fid$b_nmx = head_no >> 16;
memset( head, 0, 512 );
head->fh2$w_struclev = F11WORD( STRUCLEV );
head->fh2$w_seg_num = seg_num;
head->fh2$l_fileowner = vcbdev->home.hm2$w_volowner;
head->fh2$w_fileprot = vcbdev->home.hm2$w_fileprot;
fid_copy( &head->fh2$w_fid , fid, 0 );
head->fh2$w_fid.fid$b_rvn = 0;
if( back != NULL )
fid_copy( &head->fh2$w_backlink, back, 0 );
head->fh2$w_recattr.fat$l_efblk = F11SWAP(1);
head->fh2$l_highwater = F11LONG( 1 );
head->fh2$b_idoffset = FH2$C_LENGTH / 2;
id = (struct IDENT *) ((unsigned short *) head + head->fh2$b_idoffset);
#ifdef MINIMIZE_IDENT
head->fh2$b_mpoffset = (head->fh2$b_idoffset +
(offsetof( struct IDENT, fi2$t_filenamext ) / 2));
#else /* VMS allocates maximum len filename, which simplifies rename. Use same strategy */
head->fh2$b_mpoffset = head->fh2$b_idoffset + ( sizeof( struct IDENT ) / 2 );
memset( id->fi2$t_filenamext, ' ', sizeof( id->fi2$t_filenamext ) );
#endif
len = strlen( filename );
if( len <= sizeof( id->fi2$t_filename ) ) {
memcpy( id->fi2$t_filename, filename, len );
memset( id->fi2$t_filename + len, ' ', sizeof( id->fi2$t_filename ) - len );
} else {
memcpy( id->fi2$t_filename, filename, sizeof( id->fi2$t_filename ) );
len -= sizeof( id->fi2$t_filename );
#ifdef MINIMIZE_IDENT
memcpy( id->fi2$t_filenamext, filename + sizeof( id->fi2$t_filename ), len );
if( len & 1 )
id->fi2$t_filenamext[len] = ' ';
head->fh2$b_mpoffset += (f11byte)((len + 1) / 2);
#else
memcpy( id->fi2$t_filenamext, filename + sizeof( id->fi2$t_filename ), len );
#endif
}
id->fi2$w_revision = 0;
sys_gettim( id->fi2$q_credate );
memcpy( id->fi2$q_revdate, id->fi2$q_credate, sizeof(id->fi2$q_credate) );
head->fh2$b_acoffset = offsetof( struct HEAD, fh2$w_checksum ) / 2;
head->fh2$b_rsoffset = offsetof( struct HEAD, fh2$w_checksum ) / 2;
if( rethead != NULL )
*rethead = head;
return SS$_NORMAL;
}
/************************************************************ update_create() */
/* Create a new file */
vmscond_t update_create( struct VCB *vcb, struct fiddef *did, char *filename,
struct fiddef *fid, struct NEWFILE *attrs, struct FCB **fcb ) {
struct VIOC *vioc;
struct HEAD *head;
struct IDENT *id;
uint32_t idxblk;
register vmscond_t sts;
struct UIC dirown;
static const VMSTIME notset = VMSTIME_ZERO;
if( !(vcb->status & VCB_WRITE) )
return SS$_WRITLCK;
if( $FAILS(sts = accessfile( vcb, did, fcb, FALSE )) )
return sts;
head = (*fcb)->head;
dirown = head->fh2$l_fileowner;
if( attrs->verlimit == 0 )
attrs->verlimit = head->fh2$w_recattr.fat$w_versions;
deaccessfile( *fcb );
if( $FAILS(sts = update_addhead( vcb, filename, did, 0, fid, &vioc, &head, &idxblk )) )
return sts;
if( attrs != NULL ) {
id = (struct IDENT *)( ((f11word *)head) + F11WORD(head->fh2$b_idoffset) );
if( memcmp( attrs->credate, notset, sizeof( notset ) ) )
memcpy( id->fi2$q_credate, attrs->credate, sizeof( VMSTIME ) );
if( memcmp( attrs->revdate, notset, sizeof( notset ) ) )
memcpy( id->fi2$q_revdate, attrs->revdate, sizeof( VMSTIME ) );
if( memcmp( attrs->expdate, notset, sizeof( notset ) ) )
memcpy( id->fi2$q_expdate, attrs->expdate, sizeof( VMSTIME ) );
if( memcmp( attrs->bakdate, notset, sizeof( notset ) ) )
memcpy( id->fi2$q_bakdate, attrs->bakdate, sizeof( VMSTIME ) );
if( attrs->fileowner.uic$w_mem || attrs->fileowner.uic$w_grp )
head->fh2$l_fileowner = attrs->fileowner;
else
head->fh2$l_fileowner = dirown;
if( attrs->fileprot & (1u << 16) )
head->fh2$w_fileprot = (f11word)(attrs->fileprot);
head->fh2$w_recattr.fat$b_rtype = attrs->recattr.fat$b_rtype;
head->fh2$w_recattr.fat$b_rattrib = attrs->recattr.fat$b_rattrib;
head->fh2$w_recattr.fat$w_rsize = attrs->recattr.fat$w_rsize;
head->fh2$w_recattr.fat$b_bktsize = attrs->recattr.fat$b_bktsize;
head->fh2$w_recattr.fat$b_vfcsize = attrs->recattr.fat$b_vfcsize;
head->fh2$w_recattr.fat$w_maxrec = attrs->recattr.fat$w_maxrec;
head->fh2$w_recattr.fat$w_defext = attrs->recattr.fat$w_defext;
head->fh2$w_recattr.fat$w_gbc = attrs->recattr.fat$w_gbc;
memcpy(head->fh2$w_recattr.fat$_UU0, attrs->recattr.fat$_UU0,
sizeof( head->fh2$w_recattr.fat$_UU0 ));
head->fh2$w_recattr.fat$w_versions = attrs->recattr.fat$w_versions;
#define CRE_MODIFIABLE ( \
FH2$M_NOBACKUP | FH2$M_WRITEBACK | FH2$M_READCHECK | \
FH2$M_WRITECHECK | FH2$M_CONTIGB | FH2$M_LOCKED | \
FH2$M_CONTIG | FH2$M_SPOOL | FH2$M_DIRECTORY | \
FH2$M_NOCHARGE | FH2$M_ERASE | FH2$M_NOMOVE )
head->fh2$l_filechar = ( ( head->fh2$l_filechar & ~CRE_MODIFIABLE ) |
( attrs->filechar & CRE_MODIFIABLE ) );
#undef CRE_MODIFIABLE
if( attrs->revision != 0 )
id->fi2$w_revision = attrs->revision;
}
sts = deaccesshead( vioc, head, idxblk );
cache_flush();
if( $SUCCESSFUL(sts) && fcb != NULL) {
sts = accessfile(vcb, fid, fcb, 1);
}
#if DEBUG
printf("(%d,%d,%d) %d\n", fid->fid$w_num, fid->fid$w_seq, fid->fid$b_rvn, sts);
#endif
cache_flush();
return sts;
}
/************************************************************ update_extend() */
vmscond_t update_extend( struct FCB *fcb, uint32_t blocks, uint32_t contig ) {
register vmscond_t sts;
struct VCBDEV *vcbdev;
struct VIOC *vioc;
struct HEAD *head;
uint32_t headvbn;
struct fiddef hdrfid;
uint32_t hdrseq;
uint32_t start_pos = 0;
uint32_t block_count;
if (blocks < 1)
return SS$_BADPARAM;
if( !(fcb->status & FCB_WRITE) )
return SS$_WRITLCK;
if( fcb->wcb != NULL && cache_refcount( &fcb->wcb->cache ) != 0 ) {
printf( "Extend while WCB has a reference\n" );
abort();
}
block_count = F11WORD( fcb->head->fh2$w_recattr.fat$w_defext );
if( block_count == 0 ) {
vcbdev = RVN_TO_DEV(fcb->vcb, fcb->rvn);
if( vcbdev == NULL )
return SS$_DEVNOTMOUNT;
block_count = F11WORD(vcbdev->home.hm2$w_extend);
}
if( blocks < block_count )
blocks = block_count;
sts = SS$_NORMAL;
while( blocks > 0 && $SUCCESSFUL(sts) ) {
block_count = blocks;
if (fcb->hiblock > 0) {
uint32_t mapblk, maplen;
if( $FAILS(sts = getwindow( fcb, fcb->hiblock, &vcbdev, &mapblk,
&maplen, &hdrfid, &hdrseq)) )
return sts;
start_pos = mapblk + 1;
if( hdrseq != 0 ) {
if( $FAILS(sts = accesshead( fcb->vcb, &hdrfid, hdrseq,
&vioc, &head, &headvbn, 1 )) )
return sts;
} else {
head = fcb->head;
vioc = NULL;
}
} else {
head = fcb->head;
vioc = NULL;
start_pos = 0;
}
if (vioc == NULL) {
vcbdev = RVN_TO_DEV(fcb->vcb, fcb->rvn);
if( vcbdev == NULL )
return SS$_DEVNOTMOUNT;
}
if( start_pos == 0 ) {
start_pos = /* Randomize initial placement to help contig extends */
( ((head->fh2$w_fid.fid$w_num << 16) | head->fh2$w_fid.fid$w_seq) *
(head->fh2$w_fid.fid$b_rvn << 24) | 6781 | (head->fh2$w_fid.fid$b_nmx << 8)) %
vcbdev->max_cluster;
start_pos *= vcbdev->clustersize;
}
if( vcbdev->free_clusters == 0 || head->fh2$b_map_inuse + 4 >=
head->fh2$b_acoffset - head->fh2$b_mpoffset ) {
struct VIOC *nvioc;
struct HEAD *nhead;
uint32_t nidxblk;
if( $FAILS(sts = update_addhead(fcb->vcb, "", &head->fh2$w_fid,
head->fh2$w_seg_num+1, &head->fh2$w_ext_fid,
&nvioc, &nhead, &nidxblk)) )
return sts;
if (vioc != NULL)
deaccesshead(vioc, head, headvbn);
vioc = nvioc;
head = nhead;
headvbn = nidxblk;
vcbdev = RVN_TO_DEV(fcb->vcb, head->fh2$w_fid.fid$b_rvn);
if (vcbdev == NULL)
return SS$_DEVNOTMOUNT;
}
sts = bitmap_search( vcbdev, &start_pos, &block_count);
#if DEBUG
printf("Update_extend %d %d %d %d\n", start_pos, block_count, fcb->hiblock, vcbdev->free_clusters);
#endif
if( $SUCCESSFUL(sts) ) {
if( block_count < 1 ||
(contig && block_count < blocks) ) {
sts = SS$_DEVICEFULL;
} else {
uint32_t alloc_start, alloc_count;
alloc_start = start_pos;
alloc_count = block_count;
addmappointers( head, &start_pos, &block_count, 0 );
if( fcb->wcb != NULL )
cache_delete( &fcb->wcb->cache );
alloc_count -= block_count;
fcb->hiblock += alloc_count;
if( alloc_count >= blocks )
blocks = 0;
else
blocks -= alloc_count;
sts = bitmap_modify( vcbdev,
alloc_start / vcbdev->clustersize,
alloc_count / vcbdev->clustersize, 0 );
}
}
if( vioc != NULL )
deaccesshead(vioc, head, headvbn);
}
return sts;
}
/************************************************************** deallocfile() */
vmscond_t deallocfile( struct FCB *fcb ) {
register vmscond_t sts = SS$_NORMAL;
/*
First mark all file clusters as free in BITMAP.SYS
*/
register uint32_t vbn = 0;
for( vbn = 1; vbn <= fcb->hiblock; ) {
register vmscond_t sts = 0;
uint32_t phyblk = 0, phylen = 0;
struct VCBDEV *vcbdev = NULL;
if( $FAILS(sts = getwindow( fcb, vbn, &vcbdev, &phyblk, &phylen, NULL, NULL )) )
break;
vbn += phylen;
phyblk /= vcbdev->clustersize;
phylen = (phylen + vcbdev->clustersize -1)/vcbdev->clustersize;
if( $FAILS(sts = bitmap_modify( vcbdev, phyblk, phylen, 1 )) )
break;
}
/*
Now reset file header bit map in INDEXF.SYS and
update each of the file headers...
*/
{
uint32_t rvn = fcb->rvn;
uint32_t headvbn = fcb->headvbn;
struct HEAD *head = fcb->head;
struct VIOC *headvioc = fcb->headvioc;
while (TRUE) {
uint32_t ext_seg_num = 0;
struct fiddef extfid;
WORK_UNIT *bitmap = NULL;
struct VIOC *vioc = NULL;
register uint32_t filenum = 0;
register struct VCBDEV *vcbdev = NULL;
register uint32_t idxblk = 0;
filenum = ((size_t)head->fh2$w_fid.fid$b_nmx << 16) +
(size_t)head->fh2$w_fid.fid$w_num - 1;
vcbdev = RVN_TO_DEV(fcb->vcb, rvn);
if (vcbdev == NULL) {
sts = SS$_DEVNOTMOUNT;
break;
}
idxblk = (size_t)filenum / 4096 + (size_t)vcbdev->home.hm2$w_cluster * 4 + 1;
if( $FAILS(sts = accesschunk( vcbdev->idxfcb, idxblk, &vioc,
(char **) &bitmap, NULL, 1 )) )
break;
bitmap[(filenum % 4096) / WORK_BITS] &=
~(WORK_C( 1 ) << (filenum % WORK_BITS));
sts = deaccesschunk( vioc, idxblk, 1, TRUE );
head->fh2$l_filechar |= F11LONG( FH2$M_MARKDEL );
head->fh2$w_fid.fid$w_num = 0;
head->fh2$w_fid.fid$b_rvn = 0;
head->fh2$w_fid.fid$b_nmx = 0;
head->fh2$w_checksum = 0;
ext_seg_num++;
memcpy(&extfid, &fcb->head->fh2$w_ext_fid, sizeof(struct fiddef));
if( $FAILS(sts = deaccesshead(headvioc, NULL, headvbn)) )
break;
if (extfid.fid$b_rvn == 0) {
extfid.fid$b_rvn = rvn;
} else {
rvn = extfid.fid$b_rvn;
}
if( extfid.fid$w_num != 0 || extfid.fid$b_nmx != 0) {
if( $FAILS(sts = accesshead( fcb->vcb, &extfid , ext_seg_num,
&headvioc, &head, &headvbn, 1 )) )
break;
} else
break;
}
if( $SUCCESSFUL(sts) ) {
fcb->headvioc = NULL;
cache_untouch( &fcb->cache, TRUE );
cache_delete( &fcb->cache );
}
}
return sts;
}
/************************************************************** accesserase() */
/* accesserase: delete a file... */
vmscond_t accesserase( struct VCB * vcb, struct fiddef * fid ) {
struct FCB *fcb;
register vmscond_t sts;
struct VCBDEV *vcbdev;
vcbdev = RVN_TO_DEV(vcb, fid->fid$b_rvn);
if( vcbdev == NULL )
return SS$_DEVNOTMOUNT;
if( (fid->fid$w_num | ((uint32_t)fid->fid$b_nmx << 16)) <=
(uint32_t)F11WORD( vcbdev->home.hm2$w_resfiles ) ) {
return SS$_NOPRIV;
}
if( $SUCCESSFUL(sts = accessfile(vcb, fid, &fcb, 1)) ) {
fcb->head->fh2$l_filechar |= F11LONG(FH2$M_MARKDEL);
#if DEBUG
printf("Accesserase ... \n");
#endif
sts = deaccessfile( fcb );
}
return sts;
}
/************************************************************** update_truncate() */
vmscond_t update_truncate( struct FCB *fcb, uint32_t newsize ) {
struct HEAD *head;
f11word *mp, *ep;
#if DEBUG
vmscond_t sts;
#endif
uint32_t clustersize, eofblk, vbn = 1;
struct VCBDEV *vcbdev;
int removed = 0;
if( !(fcb->status & FCB_WRITE) )
return SS$_WRITLCK;
if( fcb->wcb != NULL && cache_refcount( &fcb->wcb->cache ) != 0 ) {
printf( "Truncate while WCB has a reference\n" );
abort();
}
head = fcb->head;
if( (vcbdev = RVN_TO_DEV( fcb->vcb, head->fh2$w_fid.fid$b_rvn )) == NULL )
return SS$_BUGCHECK;
#if DEBUG
#define release(lbn, count) \
if( (lbn) % clustersize || (count) % clustersize || !(count) ) \
abort(); \
if( $FAILS(sts = bitmap_modify( vcbdev, ((lbn)/clustersize), \
(count)/clustersize, \
TRUE )) ) \
abort();
#else
#define release(lbn, count) \
(void) bitmap_modify( vcbdev, ((lbn)/clustersize), \
(count)/clustersize, \
TRUE )
#endif
clustersize = F11WORD( vcbdev->home.hm2$w_cluster );
newsize = ( (newsize + clustersize -1) / clustersize ) * clustersize;
if( newsize == fcb->hiblock )
return SS$_NORMAL;
mp = ((f11word *)head) + head->fh2$b_mpoffset;
ep = mp + head->fh2$b_map_inuse;
while( mp < ep ) {
f11word e0;
uint32_t count, pbn, newcount;
e0 = *mp++;
e0 = F11WORD(e0);
switch( e0 & FM2$M_FORMAT3 ) {
case FM2$M_FORMAT1:
count = (e0 & 0xff);
pbn = F11WORD(*mp) | (((e0 & ~FM2$M_FORMAT3) >> 8) << 16);
mp++;
if( vbn + count <= newsize )
break;
if( vbn > newsize ) {
removed += 2;
release( pbn, count + 1 );
mp[-2] = 0;
mp[-1] = 0;
break;
}
newcount = newsize + 1 - vbn;
mp[-2] = F11WORD( (newcount -1) |
((pbn >> 8) & 0x3f00) |
FM2$M_FORMAT1 );
pbn += newcount;
release( pbn, (count + 1) - newcount );
break;
case FM2$M_FORMAT2:
count = (e0 & ~FM2$M_FORMAT3);
pbn = *mp++;
pbn = F11WORD( pbn );
pbn |= F11WORD (*mp ) << 16;
mp++;
if( vbn + count <= newsize )
break;
if( vbn > newsize ) {
removed += 3;
release( pbn, count +1 );
mp[-3] = 0;
mp[-2] = 0;
mp[-1] = 0;
break;
}
newcount = newsize + 1 - vbn;
mp[-3] = F11WORD( (newcount -1) | FM2$M_FORMAT2 );
pbn += newcount;
release( pbn, (count + 1) - newcount );
break;
case FM2$M_FORMAT3:
count = ((e0 & ~FM2$M_FORMAT3) << 16);
count |= F11WORD( *mp );
mp++;
pbn = *mp++;
pbn = F11WORD( pbn );
pbn |= F11WORD (*mp ) << 16;
mp++;
if( vbn + count <= newsize )
break;
if( vbn > newsize ) {
removed += 4;
release( pbn, count + 1 );
mp[-4] = 0;
mp[-3] = 0;
mp[-2] = 0;
mp[-1] = 0;
break;
}
newcount = newsize + 1 - vbn;
mp[-4] = F11WORD( ((newcount -1) >> 16) | FM2$M_FORMAT3 );
mp[-3] = F11WORD( (f11word)(newcount -1) );
pbn += newcount;
release( pbn, (count + 1) - newcount );
break;
default:
continue;
}
vbn += count + 1;
}
#if DEBUG
printf( "Truncate removed %u map words, end hiblk = %u, vbn = %u, newsze = %u", removed, fcb->hiblock, vbn, newsize );
#endif
head->fh2$b_map_inuse -= removed;
fcb->hiblock = newsize;
fcb->head->fh2$w_recattr.fat$l_hiblk = F11SWAP( newsize );
eofblk = F11SWAP(fcb->head->fh2$w_recattr.fat$l_efblk);
if( fcb->head->fh2$w_recattr.fat$w_ffbyte == 0 )
--eofblk;
#if DEBUG
printf( " Eofblk = %u\n", eofblk );
#endif
if( eofblk > newsize ) {
fcb->head->fh2$w_recattr.fat$l_efblk = F11SWAP( newsize + 1 );
fcb->head->fh2$w_recattr.fat$w_ffbyte = 0;
}
if( fcb->head->fh2$b_idoffset > 39 &&
fcb->head->fh2$l_highwater != 0 &&
F11LONG(fcb->head->fh2$l_highwater) > newsize + 1 )
fcb->head->fh2$l_highwater = F11LONG( newsize + 1 );
/* Recompute CONTIG by adding 0 blocks */
eofblk = 0;
addmappointers( fcb->head, &eofblk, &eofblk, 0 );
if( fcb->wcb != NULL )
cache_delete( &fcb->wcb->cache );
return SS$_NORMAL;
}