.TITLE FRAG_LIST ; ; FRAG_LIST.MAR - This program should open the index file specified ; by the user and search for files that have a certain ; number of retrieval pointers (also specified by the ; user). This program assumes that the 'process' running ; it will have read access to the index file. ; ; The first file header can be found by using the ; formula C*4+S+1 where C= CLUSTERSIZE and ; S= SIZE OF BITMAP.SYS in blocks . C & S are both ; in the home block of the disk at the following locations ; C is at byte E thru F and S is at byte 20 thru 21. ; ; Note that there are three types of retrieval ; pointer formats. i.e. 4 byte, 6 byte and 8 byte... ; but ...in writing this program I discovered that there ; seems to be another code for pointers of blocks that ; have been specifically 'placed' on the disk ... i.e. ; my program ran into difficulty reading such files... ; since this bit code is not documented, I had no choice ; but to ignore this type of file which should be a rare ; occurrence. ; ; When a file is deleted, the field for the "file id" ; is "zeroed" ... thus a file header block that contains ; a zero "file id" can be handled properly ; ; This program does encounter and processes extension ; header blocks but treats them as individual file header ; blocks... thus in a listing you might see two or more ; files with the same name very close together... this is ; not an error but rather several extension headers which ; were adjacent... you may verify that two 'files' are in ; fact the same by examining the file-id numbers of the ; files in question. ; ; Header offset definitions may be found in several places ; within SYS$LIBRARY:LIB.MLB ... $FH2DEF & $FI2DEF macros ; were used to determine the location of certain fields of ; data within the header block. ; ; This program was written in a hurry for debugging ; purpose... ; It may be enhanced further if more information comes ; to light on the file header format. ; ; Modifications ; Date Programmer Description ; 05Feb86 BO Fix final percent file fragmented calculation ; and display files in use and with undefined ; headers. Also fix the file count to be the ; actual file count used. Before that the ; program included the deleted files too. ; ; ; INPUT (SYS$INPUT) ; - DEVICE SPECIFICATION ; ; - NUMBER OF EXTENTS TO BE CONSIDERED ; ; OUTPUT (SYS$OUTPUT, SYS$PRINT) ; ; - DATA OF FILES THAT HAVE MORE THAN SPECIFIED ; NUMBER OF EXTENTS: ; ; o NAME OF THE FILE ; ; o UIC OF THE CREATOR ; ; o NUMBER OF EXTENTS ; ; o TOTAL BLOCKS ; ; o CREATION DATE AND TIME ; ; - TOTAL NUMBER OF FRAGMENTED FILES ; ; - PERCENTAGE OF DISK FILES THAT ARE FRAGMENTED ; ; NO. FRAGMENTED FILES/TOTAL FILES ON DISK ; ; - AVERAGE NUMBER OF EXTENTS PER FILE (FRAG ONLY) ; ; TOTAL EXTENTS/ TOTAL FILES FRAGMENTED ; ; - AVERAGE FILE SIZE ; ; NO. FRAGMENTED FILES/TOTAL FRAG BLOCKS ; .PAGE .SUBTITLE LOCAL ASSIGNMENTS ; CR=13 LF=10 CS=^XE ;Offset to clustersize in home block BM=^X20 ;Offset to bit map size in home block ; .PAGE .SUBTITLE LOCAL MACRO DEFINITIONS ; .LIBRARY /SYS$LIBRARY:LIB.MLB/ ; $FH2DEF ;Define symbolic offsets for file ;headers ODS-2 $LNMDEF ;Logical name service macro needs this ; .MACRO CHECK_STATUS CODE=R0,?GO ;Macro to be used after call ;to a vms service to check ;the status of the request... ;if failure, get detailed ;report from vms BLBS CODE, GO PUSHL CODE CALLS #1, G^LIB$STOP GO: .ENDM CHECK_STATUS ; ; ; .MACRO ASCTOBIN COUNT,IN_STRING,OUT_STRING ;macro to convert ascii to ;binary PUSHAL OUT_STRING ;Pass by reference PUSHAL IN_STRING ;Pass by reference PUSHL COUNT ;Pass by value CALLS #3, G^LIB$CVT_DTB ; .ENDM ASCTOBIN ; ; .PAGE .SUBTITLE LOCAL DATA STORAGE ; ; INDX_FAB: $FAB FAC=,- ;Only need to read the index file FNM= ;INDEXF.SYS contains file headers INDX_RAB: $RAB FAB=,- ;Address of the fab BKT=<0>,- ;Start with current block RAC=,- ;Process the data sequentially UBF=,- ;We'll read headers into here USZ=<512>,- ;Size of each header block ROP=,- ;Use block i/o reads RSZ=<512> ;Size of the records ; OUT_FIL_FAB: $FAB FNM=,- ORG=,- FAC=,- RAT=,- MRS=<80>,- RFM=,- FOP= OUT_FIL_RAB: $RAB FAB=,- RBF=,- RSZ=<80> HEADER_BUFF: .BLKB 512 ; Data storage for $QIOs TT: .ASCID /SYS$COMMAND/ TTCHAN: .BLKW PROMPT1: .ASCII /This program checks the device specified for 'fragmented'/- /files./- /A file is 'fragmented' if it has more than 1 'extent' so /- /keep in mind that 9 is considered a large number of /- /extents for any file./- /Thus, this program will list for you all files that have /- /a number of extents greater than or equal to the limit you/- /specify. It also shows the total blocks and date the/- /file was created./- /Note (EXTENT.DAT) will be created & submitted to SYS$PRINT./- /It will hold the information that is displayed./- /Enter a device name or logical name: / PMT1SIZ = . - PROMPT1 EXT_LIM: .BLKB 2 ; ; Will hold input from the user on the ; number of extents to use as a limit ; during the search. ; use of a string descriptor for ; LIB$CVT_DTB PROMPT2: .BYTE 10,13,10,13 .ASCII /Enter your single digit extent limit: / PMT2SIZ = . - PROMPT2 DEVNAMBUF: .BLKB 20 IOSTAT_BLK: .BLKQ ; Data storage for $CRELNM TABDESC: .ASCID /LNM$JOB/ LOGDESC: .ASCID /DEVICE/ CRELST: .WORD 0 ; Buffer length... filled in at run time .WORD LNM$_STRING ; Item list code .ADDRESS DEVNAMBUF ; Equivalence string location .LONG 0 ; Placeholder .LONG 0 ; End of item list ; Data storage for $FAO routines ; FAOLEN: .BLKW 1 ; Receives length of output string from ; $FAO .BLKW 1 ; Reserve a word here ... use FAOLEN ; later in a $QIO request FAODESC: .LONG 132 ; Descriptor for the buffer $FAO will .ADDRESS FAOBUF ; use to place its results FAOBUF: .BLKB 132 ;FORMAT: .ASCID /!AD !10 !4Exts !6 Blks !%D/ FORMAT: .ASCID /!AD !10 Exts !4 Blks !6 !%D/ INUSE: .ASCID /!AD !10 ---- File in use ---- !%D/ UNDEF_HDR: .ASCID /!AD !10 -- Undefined header - !%D/ FAOLEN2: .BLKW 1 .BLKW 1 FAODESC2: .LONG 160 .ADDRESS FAOBUF2 FAOBUF2: .BLKL 160 FORMAT2: .ASCID " !/!_!_ Total of !ZL fragmented files " - " !/!_!_ !ZL % of !ZL files on the disk " - " !/!_!_ Average number of extents per file was !ZL " - " !/!_!_ Average file size was !ZL blocks" ; ; Data storage for summary statistics ; BLOCK_TOTAL: .BLKL ; Will accum block total for each file ; EXTENT_TOTAL: .BLKL ; Will accum extent total for each file ; GRAND_TOTAL_BLOCKS: ; Will hold total of all fragm. files .BLKL ; ; FRAGM_TOTAL: .BLKL ; Counts fragmented files ; FILE_TOTAL: .BLKL ; Counts total 'files' ; Data storage for info from headers PTR_OFFSET: .BLKL ; Will hold the byte offset to the first ; retrieval pointer in a header ; MAP_AREA_WDS: .BLKL ; Will hold number of words used in the ; map area of the header... ; this value will be converted to a byte ; count during the program ; FID_NUM: .BLKW ; Used to detect hdr of deleted file ; LAST_PTR: .BLKL ; Will hold the location of the byte ; past the last pointer within a hdr ; TALLY: .BLKB ; Will count the number of ; retrieval pointers per header ; TEMP: .BLKL ; Used for converting 8 byte pointer ; data to block total .PAGE .SUBTITLE FRAGMENT CODE .PSECT CODE .ENABLE LOCAL_BLOCK .ENTRY FRAGMENT , ^M<> $ASSIGN_S CHAN= TTCHAN,- ; Get a channel number for the $QIO DEVNAM= TT CHECK_STATUS ; Clear the terminal screen CLRQ -(SP) CALLS #2,G^SCR$ERASE_PAGE $QIOW_S EFN = #2,- ; Explain the program's function and CHAN= TTCHAN,- ; get the device name to be checked FUNC= #IO$_READPROMPT,- ; in the search for fragmented IOSB= IOSTAT_BLK,- ; files P1 = DEVNAMBUF,- P2 = #20,- P5 = #PROMPT1,- P6 = #PMT1SIZ CHECK_STATUS ; Get the length of the divice string ; into the item list for $CRELNM MOVW IOSTAT_BLK+2,CRELST ; Associate logical name with device $CRELNM_S TABNAM= TABDESC,- ; specified on input LOGNAM= LOGDESC,- ITMLST= CRELST CHECK_STATUS $QIOW_S EFN = #2,- ; Get the extent limit CHAN= TTCHAN,- FUNC= #IO$_READPROMPT,- P1 = EXT_LIM,- P2 = #1,- P5 = #PROMPT2,- P6 = #PMT2SIZ CHECK_STATUS ; Convert ASCII to BINARY value ASCTOBIN #1,EXT_LIM,EXT_LIM CLRL R6 ; Initialize R6 MOVB EXT_LIM,R6 ; Save the BINARY value to R6 $OPEN FAB= ; Try to open the indexfile CHECK_STATUS ; Check for errors $CONNECT RAB= ; Connect the RAB to the FAB CHECK_STATUS ; Check for errors CLRL R7 ; Initialize a counter for blocks read ; $CREATE FAB=OUT_FIL_FAB ; Create EXTENT.DAT CHECK_STATUS $CONNECT RAB=OUT_FIL_RAB ; Form the record stream GET_BLOCK: ; $READ RAB= ; Get a block from the index file CHECK_STATUS ; Check for errors AOBLSS #2,R7,GET_BLOCK ; Increment the block counter ; Check R7... if = 1, BOOT BLOCK ; if = 2, HOME BLOCK MOVAL HEADER_BUFF, R8 ; Point to the HOME BLOCK CLRL R9 ; Initialize R9 MOVW CS(R8), R9 ; Get the clustersize into R9 CLRL R10 ; Initialize R10 MOVW BM(R8), R10 ; Get size of the Index bit map to R10 MULL2 #4,R9 ; Calculate position of first header ADDL2 R9,R10 ; ADDL2 #1,R10 ; R10 ... first header block SUBL2 #2,R10 ; Adjust for first blocks read already CLRL R9 ; ; TO_FIRST_HDR: ; $READ RAB= ; Continue to read blocks 'til reach 1st CHECK_STATUS ; header ; AOBLSS R10,R9,TO_FIRST_HDR INCL FILE_TOTAL ; Start counting files BRB 1$ ; First time through.. we've got 1st hdr ; GET_NXT_HDR: ; $READ RAB= ; Get a header block from the index file CMPL R0,#RMS$_EOF ; Check for end of file BNEQ 1$ ; If so, then we're done reading JMP STAT_SUM 1$: CHECK_STATUS CLRL MAP_AREA_WDS CLRW FID_NUM CLRL LAST_PTR CLRL PTR_OFFSET CLRL BLOCK_TOTAL ; Initialize block accumulator ; Process the information within header ; MOVAL HEADER_BUFF,R11 ; Point to the start of this header ; Is the file id zero? If so, skip it ; Get the file id MOVW FH2$W_FID_NUM(R11),FID_NUM TSTW FID_NUM ; Test it BEQL GET_NXT_HDR ; If zero, get another header, else go ; get the offset to the first pointer MOVB FH2$B_MPOFFSET(R11),PTR_OFFSET MULL2 #2 ,PTR_OFFSET ; Double it to get the byte displacement CLRL R6 MOVL PTR_OFFSET,R6 ; Store byte displacement in R6 INCL FILE_TOTAL ; Add one to file count CLRB TALLY ; Initialize the pointer counter ; ; Get the number of words in use for the ; map area ; MOVB FH2$B_MAP_INUSE(R11),MAP_AREA_WDS ; ; ; Check for bitmap.sys on RA devices TSTL MAP_AREA_WDS BNEQ 2$ JMP FILE_IN_USE ; Tell us about this file ; JMP GET_NXT_HDR ; Get next file ; 2$: MULL2 #2 ,MAP_AREA_WDS; Else change words to bytes ; Calculate byte location beyond the ; last retrieval pointer ; ADDL3 PTR_OFFSET,MAP_AREA_WDS,LAST_PTR ; CHK_NXT_PTR: ; Check the format of the header ... ; i.e. is it 4,6 or 8 bytes CMPZV #12,#4,HEADER_BUFF(R6),#4 ; The two "high order" bits in the BEQL FOUR_BYTE ; first word of each pointer contain CMPZV #12,#4,HEADER_BUFF(R6),#8 ; a code for the length of the pointer: BEQL SIX_BYTE ; 0100 = 4 4 byte pointer CMPZV #12,#4,HEADER_BUFF(R6),#^XC;1000 = 8 6 byte pointer BEQL EIGHT_BYTE ; 1100 = C 8 byte pointer JMP FUNNY_HDR ; A funny header, go tell us about it ; JMP GET_NXT_HDR ; If anything else, get next header ; FOUR_BYTE: BICW #^X FF00 ,HEADER_BUFF(R6) ADDW HEADER_BUFF(R6),BLOCK_TOTAL ; Add to the total... INCL BLOCK_TOTAL ; the number of blocks off this extent ADDL #4,R6 ; Adjust byte location for next pointer INCB TALLY ; Tally a pointer JMP END_OF_PTR_CHK ; Check for end of pointers ; SIX_BYTE: BICW #^X F000 ,HEADER_BUFF(R6) ; Strip off hdr format code ADDW HEADER_BUFF(R6),BLOCK_TOTAL ; Add to the total ... INCL BLOCK_TOTAL ; the number of blocks off this extent ADDL #6,R6 ; Adjust byte location for next pointer INCB TALLY ; Tally a pointer JMP END_OF_PTR_CHK ; Check for end of pointers ; EIGHT_BYTE: BICW #^X F000 ,HEADER_BUFF(R6) ; Strip code ADDW HEADER_BUFF+2(R6),BLOCK_TOTAL ; low order blk cnt MOVW HEADER_BUFF(R6),TEMP ; get high order... ASHL #8,TEMP,TEMP ; blk cnt... ADDL TEMP,BLOCK_TOTAL ; add to BLOCK_TOTAL INCL BLOCK_TOTAL ; the number of blocks off this extent ADDL #8,R6 ; Adjust byte location for next pointer INCB TALLY ; Tally a pointer ; END_OF_PTR_CHK: ; CMPL R6,LAST_PTR ; Compare the no. of bytes in the map ; area to the byte offset within the ; header (no. of bytes in the map area ; that have been processed) ; BEQL PTR_CHKR ; If we've run out of pointers, then ; go to a routine which checks to see ; if this file has 'too many' pointers.. ; else process more pointers JMP CHK_NXT_PTR ; ; PTR_CHKR: CMPB TALLY,EXT_LIM ; If the number of pointers (extents) ; is greater than or equal to the value ; specified by the user, then print out ; the information on this file, else ; go get the next header BGEQU DO_IO ; ; JMP GET_NXT_HDR ; Read more header blocks 'til E.O.F. ; DO_IO: ; Shoot out data on fragmented file INCL FRAGM_TOTAL ; Add to fragmented file total ADDL2 BLOCK_TOTAL,GRAND_TOTAL_BLOCKS CLRL R5 MOVL HEADER_BUFF+60,R4; Group & Member number to R4 MOVB TALLY,R5 ; TALLY of pointers to R5 ADDL2 R5,EXTENT_TOTAL ; Accumulate pointer total for later MOVL BLOCK_TOTAL,R7 ; MOVL #HEADER_BUFF+^X66,R8 ; $FAO_S CTRSTR=FORMAT,- ; Tell $FAO what the output looks like OUTLEN=FAOLEN,- ; Place to put resulting string length OUTBUF=FAODESC,-; Describe location and args. for $FAO P1 = #20 ,- P2 = #HEADER_BUFF+^X50 ,- ; File name string address P3 = R4 ,- ; UIC group & member number P5 = R5 ,- ; TALLY of pointers P6 = R7 ,- ; BLOCK_TOTAL P7 = R8 ; Creation time CHECK_STATUS ; Do I/O of the results found for each ; file JMP OUTPUT_DATA FUNNY_HDR: ; Shoot out data of funny file header CLRL R5 MOVL HEADER_BUFF+60,R4; Group & Member number to R4 MOVL #HEADER_BUFF+^X66,R8 MOVAQ UNDEF_HDR,R11 ; JMP 3$ ; Go and output ; FILE_IN_USE: ; Shoot out data of file in use CLRL R5 MOVL HEADER_BUFF+60,R4; Group & Member number to R4 MOVL #HEADER_BUFF+^X66,R8 MOVAQ INUSE,R11 ; 3$: $FAO_S CTRSTR=(R11),- ; Tell $FAO what the output looks like OUTLEN=FAOLEN,- ; Place to put resulting string length OUTBUF=FAODESC,-; Describe location and args. for $FAO P1 = #20 ,- P2 = #HEADER_BUFF+^X50 ,- ; File name string address P3 = R4 ,- ; UIC group & member number P5 = R8 ; Creation time CHECK_STATUS ; Do I/O of the results found for each ; file JMP 4$ ; OUTPUT_DATA: 4$: $QIOW_S EFN = #2,- ; CHAN= TTCHAN,- FUNC= #IO$_WRITEVBLK,- P1 = FAOBUF,- P2 = FAOLEN,- P4 = #32 CHECK_STATUS $PUT RAB= OUT_FIL_RAB ; Put record out to EXTENT.DAT CHECK_STATUS JMP GET_NXT_HDR STAT_SUM: ; Set up registers for $FAO MOVL FRAGM_TOTAL,R2 ; DIVL3 FILE_TOTAL,FRAGM_TOTAL,R3 ; % of disk files fragmented ; MULL2 #100,R3 MULL3 #100,FRAGM_TOTAL,R3 ; Make it a percent DIVL2 FILE_TOTAL,R3 ; % of dsik files fragmented MOVL FILE_TOTAL,R4 DIVL3 FRAGM_TOTAL,EXTENT_TOTAL,R5 DIVL3 FRAGM_TOTAL,GRAND_TOTAL_BLOCKS,R6 ; Format I/O $FAO_S CTRSTR=FORMAT2,-; Tell $FAO what the output looks like OUTLEN=FAOLEN2,-; Place to put resulting string length OUTBUF=FAODESC2,-; Describe location and args. for $FAO P1 = R2,- P2 = R3,- P3 = R4,- P4 = R5,- P5 = R6 ; Do I/O of statistics $QIOW_S EFN = #2,- CHAN= TTCHAN,- FUNC= #IO$_WRITEVBLK,- P1 = FAOBUF2,- P2 = FAOLEN2,- P4 = #32 CHECK_STATUS $RAB_STORE RAB= OUT_FIL_RAB,- ; Update the output buffer address RBF= FAOBUF2 CHECK_STATUS $PUT RAB= OUT_FIL_RAB ; Put record out to EXTENT.DAT CHECK_STATUS $RAB_STORE RAB= OUT_FIL_RAB,- ; Update the output buffer address RBF= FAOBUF2+80 CHECK_STATUS $PUT RAB= OUT_FIL_RAB ; Put record out to EXTENT.DAT CHECK_STATUS $DISCONNECT RAB= INDX_RAB $CLOSE FAB= INDX_FAB $DISCONNECT RAB= OUT_FIL_RAB $CLOSE FAB= OUT_FIL_FAB ; Should spool EXTENT.DAT to ; SYS$PRINT $EXIT_S ; End program ; .DISABLE LOCAL_BLOCK .END FRAGMENT ;