.TITLE RCO ;FILE RECOVERY PROGRAM ; ; MCR TASK TO RECONSTRUCT A FILE CORRUPTED BY AN ABORTED TASK. ; ; FROM ORIGINAL BY R.B. FRENCH, BOEING COMPANY ; ; CALLING SEQUENCE: ; MCR>RCO FNAME ; ; FNAME = NAME OF CORRUPTED FILE.; ; ; RCO WILL UNLOCK THE FILE, UNLESS IT IS LOCKED DUE TO WRITE ACCESS ;BY AN ACTIVE TASK. THE FILE WILL THEN BE SCANNED TO DETERMINE CERTAIN ;CRITICAL ATTRIBUTES AND THE CORRECT VALUES OF THE ATTRIBUTES WRITTEN ;INTO THE FILE HEADER. ; NOTE: RCO DOES NOT CHECK FOR FILE HEADER EXTENSIONS. THIS CAPABILITY ;WILL BE ADDED AT A LATER DATE. ; THE PRACTICAL EFFECT OF THIS IS THAT VERY LONG FILES MAY BE ;TRUNCATED BY RCO. THE LASL PROGRAM "REC" MAY ALLOW RECOVERY OF SUCH FILES. ; ; TASK BUILD FILE: ; ; RCO/PR/-FP/MU=RCO,[1,1]EXEC.STB/SS ; / ; TASK=...RCO ; UNITS=2 ; ASG=TI:2 ; PRI=60 ; STACK=32 ; UIC=[1,1] ; LIBR=SYSRES:RO ; // ; .MCALL GCMLB$,GCML$,CSI$,CSI$1,CSI$2,DIR$,QIOW$ .MCALL FINIT$,FSRSZ$,FDBDF$,FDOP$A,OPEN$U,CLOSE$,EXIT$S .MCALL FDRC$A,FHDOF$,GET$ FHDOF$ DEF$L ;DEFINE FILE HEADER OFFSETS LOCALLY SERR: JMP SYNERR FERR: JMP FCSERR RCO:: FINIT$ ;INITIALIZE FSR ; ; CHANGE HEADER TO [1,3] SO WE LOOK LIKE SYSTEM TASK ; (NO-OPED SINCE TASKBUILD WITH UIC=[1,1] DOES SAME) MOV .CRTSK,R0 ;GET ATL NODE ADDR ; MOV @#177776,-(SP) ;PSW TO STACK ; BIS #140,@#177776 ;;DISABLE TASK SWITCHING ; MOV A.HA(R0),@#177642 ;;MAP APR1 TO OUR HEADER ; MOV #7406,@#177602 ;;LENGTH, RW ; MOV @#177642,@#20024 ;;CHANGE APR1 IN HDR TOO ; MOV @#177602,@#20004 ; JSR PC,@#..ENB0 ;REENABLE TASK SWITCHING ; MOV #403,20000+H.UIC ;CHANGE HEADER UIC TO [1,3] ; ; READ AND RECODE THE COMMAND LINE. ; START:: GCML$ #GCLBLK ;GET COMMAND LINE BCC 10$ CMPB #GE.EOF,GCLBLK+G.ERR ;END OF FILE? BNE SERR ;NO, SYNTAX ERR EXIT$S ;YES, EXIT 10$: CSI$1 #CSIBLK,GCLBLK+G.CMLD+2,GCLBLK+G.CMLD ;CHECK SYNTAX BCS SERR ;SYNTAX ERR CSI$2 #CSIBLK,OUTPUT ;DECODE IT BCS SERR ;SYNTAX ERR IF NOT OK MOV #1,VBN MOV #1,VBN2 ;INITIALIZE VBN VALUES TO USE MOV #FDB,R0 ;FDB ADDR IN R0 MOV #FDB+F.FNB,R1 ;FNB ADDR IN R1 MOV #CSIBLK+C.DSDS,R2 ;DATASET POINTER IN R2 CLR R3 JSR PC,.PARSE ;PARSE THE FDB BCS FERR ;(OR LOSE) JSR PC,.FIND ;FIND THE FILE BCC 20$ CMPB #IE.NSF,FDB+F.ERR ;IS IT NO SUCH FILE? BNE FERR JMP NOFILE ;YES,TELL HIM ; ;READ THE FILE HEADER AND UNLOCK THE FILE, IF LOCKED AND NOT BEING ;ACCESSED. ; 20$: DIR$ #READ ;READ THE FILE HEADER (IO.RAT) BCS FERR ;CHECK FOR ERRORS BITB #100,HEADER+H.UCHA ; IS FILE LOCKED? BEQ COUNT ;NO, GO COUNT BLKS MOV #RDSTAT,READ+Q.IOPL+2 ;SET UP TO READ STATISTICS BLOCK DIR$ #READ ;READ STATISTICS BLOCK BCS FERR TST STAT+10 ;FILE CURRENTLY IN USE? BEQ 22$ JMP FILUSE ;IF SO, NO ACCESS NOW. (ABORT TASK FIRST!) 22$: BICB #100,HEADER+H.UCHA ; CLEAR FILE LOCKED BIT DIR$ #WRITE ;WRITE CHARACTERISTICS BLOCK BCS FERR ;LOSE IF IT FAILS MOV #RDHDR,READ+Q.IOPL+2 ;RESET QIO TO READ HEADER ; ;SCAN THE FILE HEADER MAP AREA AND COUNT THE NUMBER OF BLOCKS ACTUALLY ;ALLOCATED. ; COUNT: ; MOV #HEADER,R1 ;HEADER ADDRESS ; MOVB H.MPOF(R1),R2 ;OFFSET TO MAP AREA ; BIC #177400,R2 ;CLEAR SIGN EXTEND IF ANY ; ADD R2,R1 ;MAP AREA ADDRESS MOV #HEADER+134,R1 ;MAP AREA OFFSETS TO R1 MOV R1,R2 ;AND R2 MOVB M.USE(R2),R2 ;NUMBER OF POINTERS IN R2 BIC #177400,R2 ; BNE 5$ JMP FILEMP ;FILE EMPTY IF 0 5$: ASR R2 ADD #M.RTRV+1,R1 ;START OF POINTERS IN R1 CLR R4 10$: MOVB (R1),R3 ;BLOCK COUNT-1 IN R3 BIC #177400,R3 ;REMOVE D**NED SIGN EXTEND INC R3 ;MAKE IT BLOCK COUNT ADD R3,R4 ;ADD TO TOTAL COUNT OF BLKS ADD #4,R1 ;STEP TO NEXT POINTER SOB R2,10$ ;DO THEM ALL (IN THIS HDR) ; ;OPEN THE FILE AND PUT ACTUAL FILE ATTRIBUTES INTO THE FDB ; OPEN: OPEN$U #FDB ;OPEN THE FILE BCC 5$ JMP FCSERR 5$: CLR FDB+F.HIBK ;PUT BLOCK COUNT IN F.HIBK MOV R4,FDB+F.HIBK+2 ;TO 32 BITS (16 MAX USED) CLR FDB+F.EFBK ;PUT BLOCK COUNT IN F.EFBK MOV R4,FDB+F.EFBK+2 ;AGAIN TO 32 BITS TST FDB+F.FFBY ;END-OF-FILE BYTE DEFINED? BNE 10$ ;SKIP IF SO... MOV #1000,FDB+F.FFBY ;IF NOT SAY 1000 10$: TST FDB+F.RSIZ ;IS RECORD SIZE DEFINED? ;10$: BITB #FD.RWM,FDB+F.RACC ;BLOCK MODE ACCESS? ; BNE CLOSE ;THEN JUST CLOSE THE FILE ; TST FDB+F.RSIZ ;IS RECORD SIZE DEFINED? BNE 20$ MOV #1000,FDB+F.RSIZ ;IF NOT,MAKE IT 1000 BYTES OCTAL (1 BLK) 20$: CMPB #R.FIX,FDB+F.NRBD ;FIXED-LENGTH RECORDS? BEQ CLOSE ;IF SO, JUST CLOSE THE FILE ;HERE AVOID SPURIOUS EOF BY CLOSE AND RE-OPEN MOV #FDB+F.FNB,R1 ;CLOSE AND MOV #HEADER,R2 MOV #30.,R3 50$: MOVB (R1)+,(R2)+ ;SAVE FILE ID DEC R3 BGT 50$ CLOSE$ #FDB ; MOV #FDB+F.FNB,R1 ;RE-OPEN MOV #HEADER,R2 MOV #30.,R3 60$: MOVB (R2)+,(R1)+ ;COPY FILE BLOCK DEC R3 BGT 60$ OPEN$U #FDB ;OPEN, OTHERWISE GET EOF ON 1ST GET BCS FCSERR ; ;SCAN THE FILE TO FIND OUT MAXIMUM RECORD SIZE. CONTINUE SCAN UNTIL ;AN ERROR OCCURS. (ANY ERROR, SO BEWARE IF YOUR HARDWARE IS FLAKY!) CLR R1 SCAN: GET$ ;GET A RECORD BCS 25$ ;CHECK FOR ERROR MOV FDB+F.VBN+2,R3 ;VIRTUAL BLOCK # MOV FDB+F.NRBD+2,R2 ;POINTER TO LAST RECORD CMP R2,FDB+F.BDB ;IN BLOCK OR USER BUFFER? BLT 10$ SUB FDB+F.BDB,R2 ;SUBTRACT START OF BLOCK BUFFER SUB #S.BFHD,R2 ;ACCOUNT FOR BUFFER HEADER SUB #2,R2 ;ACCOUNT FOR COUNTER MOV R2,FFB ;SAVE FREE BYTE NEXT MOV R3,VBN ;AND BLOCK OF RECORD 10$: MOV FDB+F.NRBD,R4 ;RECORD SIZE INC R4 ;EVENIZE BY ROUNDING UP BIC #1,R4 ;AND ZOTTING LOW BIT MOV FFB,R5 ADD R4,FFB ;1ST FREE BYTE AFTER THIS ADD #2,FFB ;RECORD 15$: CMP FFB,#1000 ;NEXT BLOCK? BLE 20$ MOV R5,FFB2 ;YES, LAST GOOD BLOCK MOV VBN,VBN2 ;AND BYTE 17$: SUB #1000,FFB ;ADJUST FREE BYTE # TO INSIDE BLOCK ADD #1,VBN ;BUMP VIRTUAL BLOCK CMP FFB,#1000 ;STILL OUTSIDE BLOCK? (ZOUNDS!) BGT 17$ ;HOPE RECSIZE NEVER OVER 15 BITS! 20$: CMP R1,R4 ;RECORD GREATER? BGE SCAN ;NO, RETRY MOV R4,R1 ;YES BR SCAN 25$: CMPB #IE.EOF,FDB+F.ERR ;END OF FILE? BEQ 30$ ;IF SO, WRAP IT UP MOV FFB2,FDB+F.FFBY ;NO, EOF TO LAST GOOD BLOCK MOV FDB+F.VBN,FDB+F.EFBK ;SAVE H.O. 16 BITS OF VIRTUAL BLK# MOV VBN2,FDB+F.EFBK+2 ; AND BYTE BR 40$ 30$: MOV FFB,FDB+F.FFBY MOV FDB+F.VBN,FDB+F.EFBK ;SAVE H.O. 16 BITS OF VIRT BLK# MOV VBN,FDB+F.EFBK+2 40$: MOV R1,FDB+F.RSIZ ;SAVE LARGEST RECORD SIZE IN FDB BR CLOSE VBN: .WORD 1 VBN2: .WORD 1 FFB: .WORD 0 FFB2: .WORD 0 ; ;PUT CORRECT ATTRIBUTE VALUES IN THE FDB AND CLOSE THE FILE. ; CLOSE: CLOSE$ #FDB ;CLOSE THE FILE (DATA IN FDB ALREADY...) JMP START ;LOOK FOR MORE COMMANDS ; ;ERROR MESSAGES ; ; NOFILE: MOV #NFLMSG,QIOW+Q.IOPL MOV #14.,QIOW+Q.IOPL+2 BR SYNERR FILEMP: MOV #FLEMSG,QIOW+Q.IOPL MOV #12.,QIOW+Q.IOPL+2 BR SYNERR FILUSE: MOV #FLUMSG,QIOW+Q.IOPL;FILE ACCESSED BY ACTIVE TSK MOV #21.,QIOW+Q.IOPL+2 BR SYNERR FCSERR: MOV #FCSMSG,QIOW+Q.IOPL;FCS ERR MOV #15.,QIOW+Q.IOPL+2 MOV #FCSMSG+13,R0 MOVB FDB+F.ERR,R1 CLR R2 JSR PC,$CBDSG SYNERR: DIR$ #QIOW MOV #SYNMSG,QIOW+Q.IOPL MOV #14.,QIOW+Q.IOPL+2 ;RESET SYNTAX MSG FOR NEXT LINES JMP START ;MORE COMMANDS? GO SEE... QIOW: QIOW$ IO.WVB,2,1,,,, SYNMSG: .ASCII <12>/SYNTAX ERROR/<15> FCSMSG: .ASCII <12>/FCS ERROR /<15> FLUMSG: .ASCII <12>/FILE BEING ACCESSED/<15> FLEMSG: .ASCII <12>/FILE EMPTY/<15> NFLMSG: .ASCII <12>/NO SUCH FILE/<15> .EVEN ; ;COMMAND LINE MACROS AND DATA BLOCKS ; GCLBLK: GCMLB$ 1,RCO,HEADER,2 CSI$ .EVEN CSIBLK: .BLKB C.SIZE ;CSI BLOCK .EVEN ; ;BLOCKS TO READ/WRITE HEADER INFORMATION ; READ: QIOW$ IO.RAT,1,1,,,, WRITE: QIOW$ IO.WAT,1,1,,,, RDHDR: .BYTE -12,0 ;BLOCK TO READ FILE HEADER .WORD HEADER ;ADDR TO PUT DATA IN .WORD 0 ;LIST TERMINATOR RDSTAT: .BYTE -11,12 ;BLOCK TO READ STATISTICS BLOCK .WORD STAT ;DATA AREA .WORD 0 ;LIST TERMINATOR WRCHAR: .BYTE 3,1 ;BLOCK TO WRITE FILE CHARACTERISTICS .WORD HEADER+H.UCHA ;(WRITES ONLY PART OF BUFFER) .WORD 0 ;LIST TERMINATOR HEADER: .BLKB 1000 .WORD 0 ;SAFETY... STAT: .BLKW 5 ; ;I/O MACROS AND DATA BLOCKS FSRSZ$ 1 ;ONE LUN ONLY FDB: FDBDF$ ;START OF FDB FDOP$A 1,CSIBLK+C.DSDS FDRC$A FD.PLC,HEADER,1000 ;USE HEADER BLOCK AS I/O BUFFER ; .END RCO ;THAT'S ALL FOLKS...