/* 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 #include #include #include #include #include "access.h" #include "device.h" #include "initvol.h" #include "ods2.h" #include "phyio.h" #include "ssdef.h" #include "stsdef.h" #ifdef VMS #include #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; }