.title Files Program to Read Index Header File .ident /2.4/ ; File: FILES.MAR ;++ ; Facility: ; Utility to collect information about the size and ownership ; of files. ; ; Abstract: ; This program examines the file header file (INDEXF.SYS) ; to retrieve information about the largest files strored ; on disk and who owns them. Back link pointers in the ; INDESF.SYS file are followed to obtain a complete ; directory specification. ; ; Environment: ; Needs SYSPRV. ; ; Author: ; Mark Oakley, Battelle Columbus Labs, 11-Feb-1984 ; ; Modifications: ; ; 11-Mar-1984 Mark Oakley Revised to use mapping routines ; to access file headers. ; ; 15-Mar-1984 Mark Oakley Fixed bug in check of member uic. ; ; 22-Mar-1984 Mark Oakley Added signalling for command line ; syntax errors. ; ; 7-Apr-1984 Mark Oakley Fixed to print files equal to or ; greater that minimum size. Only ; files greater than minimum were ; printed previously. ; ; 17-Feb-1985 Mark Oakley Revised to work with V4 file ; headers. ; ; 19-Nov-1985 Mark Oakley Added capability to handle character ; uic's on command line. ; ; 12-Sep-1986 Mark Oakley Added check for maximum file size, ; expired files. ; ; 15-Sep-1986 Mark Oakley Added check for files whose blocks ; allocated exceeds blocks used by ; some amount. ; ;-- .sbttl Symbols .library /SYS$LIBRARY:LIB.MLB/ $dvidef ; Device definitions. $fabdef ; File access block definitions. $fh2def ; File-header definitions. $fi2def ; File-header-id definitions. $jpidef ; Job/process definitions. $hm2def ; Home block definitions. $secdef ; Mapping section definitions. $ssdef ; Termination definitions. $tpadef ; LIB$TPARSE definitions. ; ; Note that $fh2def lacks the following definitions: ; fh2$t_fname = ^x50 fh2$l_alloc = ^x18 ; Offset to blocks allocated. fh2$l_used = ^x1c ; Offset to blocks used. ; ; Macro to help set up fabs for each volume in a volume set. ; .macro mfab $fab dnm=<[000000]INDEXF.SYS>,- fna=devnam_buf,- fop= .endm mfab ; ; Macro to handle return codes. ; .macro on_err there,?here blbs r0,here brw there here: .endm on_err .sbttl Command line, prompts data .psect files_data,rd,wrt,noexe,long,shr,pic command_line_desc: .word command_line_buf_siz .word 0 .address command_line_buf command_line_buf: ; Store the command line here. .blkb 80 command_line_buf_siz = . - command_line_buf command_line_len: ; Store the command line length here. .blkl 1 parse_blk: ; Parse block for LIB$TPARSE to .long tpa$k_count0 ; parse command line. .long tpa$m_abbrev ; Permit unambiguous abbreviations. .blkb tpa$k_length0-8 min_file_size: ; Search for files larger than .long 0 ; this size. (Use this default until ; we discover otherwise.) max_file_size: ; Search for files smaller than .long 2147483647 ; this size. (Use this default until ; we discover otherwise.) grp_uic: ; Search for files with this group .long -1 ; uic. mem_uic: ; Search for files with this group .long -1 ; uic. expired_flag: ; See if we should check for JUST .long 0 ; expired files. (Default is no.) current_date_time: ; Use current date and time to check .blkq 1 ; for expired files. difference_flag: ; See if we should check for large .long 0 ; diff between used and allocated. difference_amount: ; Add this amount to used, to compare .blkl 1 ; with allocated. .save_psect .psect pflags pic,ovr,rel,gbl,shr,noexe,rd,wrt,long first_call: ; Flag for WRITE_IT routine. .long 0 full: ; Flag to specify full (or brief) .long 0 ; printing. .restore_psect .sbttl Device data default_disk: ; Use default if no disk given. .ascid /SYS$DISK/ init_dvi_itmlst: ; Item list to get the root device .word devnam_buf_siz ; name of a volume set. .word dvi$_rootdevnam .address devnam_buf .address devnam_len .word 4 .word dvi$_volcount .address volset_cnt .long 0 .long 0 next_dvi_itmlst: ; Item list to get succeeding .word next_devnam_buf_siz ; device names of a volume set, .word dvi$_nextdevnam ; disk cluster size, and maximum .address next_devnam_buf ; number of files allowed on .address next_devnam_len ; device. .word 4 .word dvi$_cluster .address cluster_size .long 0 .word 4 .word dvi$_maxfiles .address max_files .long 0 .long 0 devnam_desc: .word devnam_buf_siz .word 0 .address devnam_buf devnam_buf: ; Store the device name here. .blkb 50 devnam_buf_siz = . - devnam_buf devnam_len: ; Store the device name length here. .blkl 1 next_devnam_buf: .blkb 50 next_devnam_buf_siz = . - next_devnam_buf next_devnam_len: .blkl 1 cluster_size: ; Disk cluster factor. .blkl 1 max_files: ; Maximum number of files .blkl 1 ; on disk. ifb_channel: ; Table of channels to each INDEXF.SYS .blkw 10 ; on each volume in set. ifb_iosb: ; Status for INDEXF.SYS operations. .blkq 1 volset_cnt: ; Number of volumes in a volume set. .blkl 1 directory_desc: ; Directory spec will be .word 0 ; stored here. .word 0 .address directory_buf directory_buf: .blkb 255 directory_buf_size = . - directory_buf file_desc: .word 0 .word 0 .address file_buf file_buf: .blkb fi2$s_filename+fi2$s_filenamext file_buf_siz = . - file_buf .sbttl Table of Fab's for Volume Set .psect fab_table,,rd,wrt,noexe,page,shr,pic ; ; Allow up to 10 volumes in a volume set. ; indexf_fab_1: mfab indexf_fab_2: mfab indexf_fab_3: mfab indexf_fab_4: mfab indexf_fab_5: mfab indexf_fab_6: mfab indexf_fab_7: mfab indexf_fab_8: mfab indexf_fab_9: mfab indexf_fab_10: mfab fab_index_table: .address indexf_fab_1 .address indexf_fab_2 .address indexf_fab_3 .address indexf_fab_4 .address indexf_fab_5 .address indexf_fab_6 .address indexf_fab_7 .address indexf_fab_8 .address indexf_fab_9 .address indexf_fab_10 .sbttl Index File Bitmap Buffer .psect indexf_file_bitmap,rd,wrt,noexe,page,shr,pic index_file_bitmap: .blkb <60*512> ; Allow for bitmap sizes up to 60 blocks ; (29760 files) per volume. first_ifb_vbn: ; Table of vbn's which are the start of .blkl 10 ; bitmap in each INDEXF.SYS for each volume. header_offset: ; Table of offset's to get to the start of ; headers in each INDEXF.SYS for each volume. last_ifb_vbn: ; Table of vbn's which are the end of bitmap .blkl 10 ; in each INDEXF.SYS for each volume. .psect directory_header,rd,wrt,noexe,page,shr,pic ; ; Store file header for directory here. This is used to follow ; directiry back-links to obtain a complete directory spec for ; the file. ; dir_hd_buf: .blkb 512 .sbttl Mapped Section Data Structures input_addr: ; sec$m_expreg will be set, so setting .long ^x20000 ; both address's equal guarantees that .long ^x20000 ; no more memory is mapped than needed. return_addr: ; Address of mapped area. This area .blkl 2 ; will hold up to 4000 file headers. mapped_pages: ; Max number of pages mapped at once. .long 4000 pf_cluster: ; Number of pages to bring into memory .long 128 ; if a page fault occurs. sect_flag = ; Section is copy-on-reference and ; expand memory region as needed. .sbttl Parse Tables ; ; State and transition instructions on how to parse command line. ; .psect files_parse,rd,nowrt,exe,long,shr,pic $init_state files_state,files_key $state start $tran tpa$_eos,tpa$_exit ; End of string here is success. $tran '/' ; Another qualifier detected. $tran tpa$_symbol,tpa$_exit,- ; ,,devnam_desc $state ; Determine which qualifier. $tran 'SIZE',get_size $tran 'UIC',get_uic $tran 'EXPIRED',start,,1,expired_flag $tran 'DIFFERENCE',get_difference,,1,difference_flag $tran 'FULL',start,,1,full $state get_uic ; Parse for getting the uic. $tran '=' $state $tran '[' $state $tran '*',comma_check,,-1,grp_uic ; Check for [* $tran tpa$_octal,get_mem_uic,,,grp_uic ; Check for [n $tran tpa$_symbol,end_uic,cvt_chr_uic ; Check for [x $state comma_check $tran ']',start,,-1,mem_uic ; Check for [*] $tran <','>,have_comma ; Check for [*, $state get_mem_uic $tran <','> ; Check for [n, $state have_comma $tran tpa$_octal,,,,mem_uic ; Check for [*,n or [n,n $tran '*',,,-1,mem_uic ; Check for [*,* or [n,* $state end_uic $tran ']',start $state get_size ; Parse for getting the minimum $tran '=' ; and maximum file sizes. $state $tran tpa$_decimal,start,,,min_file_size $tran '(' $state $tran <','>,get_max_size $tran tpa$_decimal,,,,min_file_size ; Set minimum file size. $state $tran ')',start $tran <','> $state get_max_size $tran tpa$_decimal,,,,max_file_size ; Set maximum file size. $state $tran ')',start $state get_difference $tran '=' $state $tran tpa$_decimal,start,,,difference_amount $end_state .sbttl LIB$TPARSE Action Routine for Character UIC. ; ; This action routine processes a character uic. $ASCTOID is called to ; perform the conversion. If the system service fails, or a non-uic id ; is detected, then an error status is returned. ; .entry cvt_chr_uic,^m $asctoid_s - ; Convert character uic to numeric. name=parse_blk+tpa$l_tokencnt,- id=grp_uic on_err cvt_chr_uic_exit bicl3 #^x3fffffff,grp_uic,r5 ; Did we get a non-uic id? beqlu 20$ pushal parse_blk+- ; Yes, capture the string and tpa$l_tokencnt ; signal the error. pushl #1 pushl #fls_notuicid calls #3,g^lib$signal 20$: movzwl grp_uic,mem_uic ; Put the group and member uic's into ashl #<-16>,grp_uic,grp_uic ; separate longwords. movl #ss$_normal,r0 cvt_chr_uic_exit: ret .sbttl Main program .psect files_code,rd,nowrt,exe,long,shr,pic .entry files,^m jsb parse_comm_line ; Override default values as necessary. on_err main_exit jsb compute_offsets ; Compute offsets to 1st file header on_err main_exit ; and other info. jsb search_bitmap ; Search for valid headers, output on_err main_exit ; results. main_exit: ret .sbttl Parse Command Line ; ; This routine scans the command line for uic, file size, ; and a device name. ; parse_comm_line: movq default_disk,- ; Assume device is default device devnam_desc ; until we find otherwise. pushl #0 ; See what's on the command line. pushal command_line_len pushl #0 pushal command_line_desc calls #4,g^lib$get_foreign on_err parse_comm_line_exit movzwl command_line_len,- ; Set up for LIB$TPARSE. parse_blk+tpa$l_stringcnt moval command_line_buf,- parse_blk+tpa$l_stringptr pushal files_key pushal files_state pushal parse_blk calls #3,g^lib$tparse cmpl #lib$_syntaxerr,r0 ; Did we get a syntax error? bneq 30$ pushal parse_blk+- ; Yes, capture the string and tpa$l_tokencnt ; signal the error. pushl #1 pushl #fls_syntaxerr calls #3,g^lib$signal 30$: on_err parse_comm_line_exit $getdvi_s - ; Get root volume name. devnam=devnam_desc,- itmlst=init_dvi_itmlst on_err parse_comm_line_exit movw devnam_len,devnam_desc ; Must restore what we or LIB$TPARSE moval devnam_buf,- ; did to this descriptor. devnam_desc+4 $gettim_s - ; Get current date and time. timadr=current_date_time on_err parse_comm_line_exit parse_comm_line_exit: rsb .sbttl Compute offsets ; ; This routine computes the first block in INDEXF.SYS which contains ; a header. ; compute_offsets: clrl r5 ; r5 holds volume number. next_vol: $getdvi_s - ; Get infomation about maximum files, devnam=devnam_desc,- ; cluster size, and next volume in set. itmlst=next_dvi_itmlst on_err compute_header_exit skip_on_first: movl r5,r4 incl r5 ; Set r5 to point to next volume. movl fab_index_table[r4],r4 ; Compute fab address. movb devnam_len,- ; Remember to set the length of the fab$b_fns(r4) ; device name in the fab. ; ; Compute the start of the file headers, which is ; 4 * disk cluster factor + bit map file size. ; divl3 #4096,max_files,r2 ; Bitmap file size is max files / 4096 incl r2 ; + 1. mull3 #4,cluster_size,r3 addl2 r2,r3 ; File header offset in r3. ; ; Save start and end vbn's of index bit file map. ; movl r3,last_ifb_vbn[r5] subl2 r2,r3 incl r3 movl r3,first_ifb_vbn[r5] $open fab=(r4) ; Open the file for "user" file on_err read_bitmap_exit ; processing. movw fab$l_stv(r4),- ; Remember the channel number. ifb_channel[r5] cmpl r5,volset_cnt ; End of volumes? bgeq 10$ pushr #^m ; Save reg for doing a movc3 op. movc3 next_devnam_len,- ; Set up for doing $getdvi on next next_devnam_buf,- ; volume in set. devnam_buf popr #^m movl next_devnam_len,- ; Make sure device name length is ok. devnam_desc movl next_devnam_len,- devnam_len brw next_vol 10$: ; ; Take care of case where we are processing ; just one device (that is, not a volume set). ; movl header_offset+4,header_offset movw ifb_channel+2,ifb_channel movl first_ifb_vbn+4,first_ifb_vbn compute_header_exit: rsb .sbttl Read Index Bitfile Map ; ; Read the index bitfile map in INDEXF.SYS. ; read_bitmap: movl last_ifb_vbn[r5],r3 ; Number of bytes to read is: subl2 first_ifb_vbn[r5],r3 ; (last vbn - first vbn + 1) * 512. incl r3 mull2 #512,r3 $qiow_s chan=ifb_channel[r5],- ; Read the bitmap. func=#io$_readvblk,- iosb=ifb_iosb,- p1=index_file_bitmap,- ; Read buffer. p2=r3,- ; Number of bytes to read. p3=first_ifb_vbn[r5] ; Where to start reading. on_err read_bitmap_exit movzwl ifb_iosb,r0 on_err read_bitmap_exit read_bitmap_exit: rsb .sbttl Search the Index Bitmap File ; ; This routine searches the index header bitmap for valid headers. ; When a valid header is found, it is read from disk. Selected ; fields are printed. ; search_bitmap: movl #1,r5 ; r5 is volume pointer. 10$: jsb read_bitmap on_err search_bitmap_exit clrl r4 ; Initialize max header number. clrl r10 ; Pointer into ifb. moval index_file_bitmap,r11 ; Get base addr of ifb. subl3 first_ifb_vbn[r5],- ; r6 will hold the size last_ifb_vbn[r5],r6 ; (in long words) of the decl r6 ; index bitmap file. mull2 #<4096/32>,r6 incl r6 20$: movl (r11)[r10],r9 ; Get next entry in ifb. clrl r8 ; Bit pointer into entry. movl #32,r7 ; Size of entry to search, in bits. 30$: ffs r8,r7,r9,r8 ; Look for a set bit. beql 40$ ; Is there a bit set? jsb read_write_header ; Yes, get information on this header. on_err search_bitmap_exit subl3 r8,#32,r7 ; Any more bits to check bgtr 30$ ; in this entry? 40$: aobleq r6,r10,20$ ; Get next entry if max not reached. aobleq volset_cnt,r5,10$ ; Next volume calls #0,g^write_totals ; Write total blocks allocated and on_err search_bitmap_exit ; used. search_bitmap_exit: rsb .sbttl Read and Write Header Information ; ; This routine reads a file header from disk and outputs selected ; information about the header. ; read_write_header: ; ; Compute page offset in memory of where the header is mapped. ; incl r8 ; Compute file id as (bit pos +1) + movl r10,r2 ; 32 * (number of bitmap long word). ashl #5,r2,r2 ; addl2 r8,r2 ; r2 has file number. cmpl r4,r2 ; Is this file header in memory? blss 40$ brw in_memory ; Yes. 40$: tstl return_addr ; No, see if we need to perform beql 60$ ; any "unmapping" first. $deltva_s - inadr=return_addr on_err read_write_header_exit 60$: addl3 header_offset[r5],r2,r1 ; Get where to start read from disk. $crmpsc_s - ; Bring the headers into memory. inadr=input_addr,- retadr=return_addr,- flags=#sect_flag,- chan=ifb_channel[r5],- pagcnt=mapped_pages,- vbn=r1,- pfc=pf_cluster cmpl #ss$_endoffile,r0 ; Did we reach end-of-file? bneq 80$ movl #32,r8 ; Yes, fix things up so that addl3 #1,r6,r10 ; we move on to the next volume clrq return_addr ; (if there is one). movl #ss$_normal,r0 brw read_write_header_exit 80$: on_err read_write_header_exit subl3 #511,return_addr+4,r3 ; r3 has start addr of max hdr. subl3 return_addr,- ; Compute highest numbered file number return_addr+4,r4 ; in memory. ashl #-9,r4,r4 addl3 r2,r4,r4 in_memory: subl3 r2,r4,r2 ; Header location in memory is: ashl #9,r2,r2 ; loc = r3 - 512 * (max_files - r2) subl3 r2,r3,r2 ; ; Check for a valid header. ; tstw fh2$w_fid(r2) ; Is the file number 0? bneq 20$ brw read_write_header_exit ; Yes, this is not a valid header. 20$: tstw fh2$w_seg_num(r2) ; Is the extension segment number 0? beql 30$ brw read_write_header_exit ; No, this is not a valid header. 30$: tstw fh2$w_checksum(r2) ; Is the checksum 0? bneq 40$ brw read_write_header_exit ; Yes, this is not a valid header. 40$: ; ; Header is valid, does file meet our criteria for reporting? ; rotl #16,fh2$l_alloc(r2),r1 ; Is file large enough? cmpl min_file_size,r1 bleq 50$ brw read_write_header_exit 50$: cmpl max_file_size,r1 ; Is file small enough? bgeq 60$ brw read_write_header_exit ; No, don't report. 60$: tstl difference_flag ; Should we check blocks alloc/used? beql 70$ rotl #16,fh2$l_used(r2),r0 ; Get used blocks. addl2 difference_amount,r0 ; Add in difference amount, and cmpl r1,r0 ; compare with allocated. bgtr 70$ ; Is allocated still larger? movl #ss$_normal,r0 brw read_write_header_exit ; No, don't report. 70$: movl #ss$_normal,r0 ; Restore R0, if it was changed. tstl grp_uic ; Should we test file group uic? blss 80$ cmpw grp_uic,- ; Yes, do the group uic's match? fh2$w_uicgroup(r2) beql 80$ ; Yes, keep on checking. brw read_write_header_exit ; No, don't report. 80$: tstl mem_uic ; Should we test member uic? blss 90$ cmpw mem_uic,- ; Yes, do the member uic's match? fh2$w_uicmember(r2) beql 90$ ; Yes. brw read_write_header_exit ; No, don't report. 90$: tstl expired_flag ; Should we check for expired file? beql 100$ movzbl fh2$b_idoffset(r2),r1 ; Get id offset and convert mull2 #2,r1 ; to bytes. addl2 r2,r1 movq fi2$q_expdate(r1),r0 ; Get file expiration date. subl2 current_date_time,r0 ; File expiration date must be less than sbwc current_date_time+4,r1 ; than current date and time, for file blss 100$ ; to be expired. movl #ss$_normal,r0 brw read_write_header_exit ; No, don't report. 100$: ; ; Get back-link of file to determine complete directory spec. ; dir_bkl: pushr #^m movzwl fh2$w_bk_fidrvn(r2),r10 ; If the file and directory are tstl r10 ; on the same volume, then the bgtr 10$ ; fh2$w_bk_fidrvn field will be movl r5,r10 ; zero. Thus, use the file rvn. 10$: moval dir_hd_buf,r8 movw #1,directory_desc movb #^a/[/,directory_buf ; Start dir spec with a "[". moval directory_desc,r7 addl2 #1,4(r7) movzwl fh2$w_backlink(r2),-(sp) ; Trace backlinks to see which calls #1,g^bk_link ; directory the file is in. moval directory_buf,4(r7) addl3 (r7),4(r7),r6 cmpw directory_desc,#1 bgtr 20$ incl r6 incw directory_desc 20$: decl r6 movb #^a/]/,(r6) ; End dir spec with a "]". write_header_info: pushl r5 ; Pass RVN. R5 must not ; get destroyed! pushal fh2$w_fid_seq(r2) ; Pass sequence number. pushal directory_desc movl r2,r6 ; Will need R2 for MOVC3. pushaw fh2$w_fid(r6) movzbl fh2$b_idoffset(r6),r7 ; Get word offset to file name. mull2 #2,r7 ; Convert to byte offset. addl2 r6,r7 ; Add in header address. movc3 #fi2$s_filename,- ; Move first part of filename fi2$t_filename(r7),- ; to file name buffer. file_buf movc3 #fi2$s_filenamext,- ; Move last part of filename fi2$t_filenamext(r7),- ; to file name buffer. (r3) ; R3 got set by previous MOVC3. pushab file_buf pushal fh2$l_alloc(r6) pushal fh2$l_used(r6) pushaw fh2$w_uicmember(r6) pushaw fh2$w_uicgroup(r6) calls #9,write_it on_err read_write_header_exit popr #^m read_write_header_exit: rsb .sbttl Follow Backlinks ; ; Recursive routine to follow directory backlinks. ; .entry bk_link,^m addl3 header_offset[r10],- ; Compute block which contains 4(ap),r9 ; the file header for the dir. $qiow_s chan=ifb_channel[r10],- ; Read a block. func=#io$_readvblk,- p1=(r8),- p2=#512,- p3=r9 on_err bk_link_exit movzbl fh2$b_idoffset(r8),r11 ; Get word offset to id. mull2 #2,r11 ; Compute to bytes. addl2 r8,r11 ; R11 points to start of id. movc3 #fi2$s_filename,- ; Move first part of filename fi2$t_filename(r11),- ; into buffer. file_buf movc3 #fi2$s_filenamext,- ; Move last part of filename, fi2$t_filenamext(r11),- ; this instruction MUST (r3) ; IMMEDIATELY follow prev instr. locc #^a/./,#,- file_buf ; Compute length of this subl2 #,- r0 ; portion of directory mnegl r0,r6 ; string. cmpw fh2$w_backlink(r8),4(ap) ; Are we at [000000] ? beql 20$ subl2 r6,sp ; Make room for directory movc3 r6,file_buf,(sp) ; name and length. pushl r6 movzwl fh2$w_backlink(r8),-(sp) ; No, keep tracing. tstw fh2$w_bk_fidrvn(r8) beql 10$ movzwl fh2$w_bk_fidrvn(r8),r10 10$: calls #1,g^bk_link on_err bk_link_exit 20$: movl (sp)+,r6 movc3 r6,(sp),@4(r7) ; Adjust descriptor. addl2 r6,sp addl2 r6,(r7) addl2 r6,4(r7) movl #ss$_normal,r0 ; Indicate success. bk_link_exit: ret .end files