.TITLE UNDELETE .IDENT /01/ .NLIST BEX,TOC .ENABL LC .PSECT ; ; VERSION 01 ; ; John Hayes 9 - SEP - 81 ; ; Originally written by Richard Kirkman and appeared in July 1981 Decus ; RSX11M SIG newsletter, THE MULTI-TASKER. ; ; MODIFIED BY: ; ; The function of this program is to attempt to recover a recently deleted ; file. ; ; The program will accept a command line with input and output disks (which ; must be different) and an input UIC. It will then scan the input disk ; indexfile for all deleted files with that UIC. Will print error message ; (warning) if search of bitmap shows that a file block has been reused. ; Will use IO.RLB from privledged task to read all blocks of the file and ; then create new file in owners UIC on output device. ; ; This program will only succeed in recovering a file, if the file header ; has not been reused. It's optimum use would be when a single user ; accidentally deletes a file and immediately stops all writing to the ; volume, to avoid loosing either the file header or any of the other ; blocks belonging to the deleted file. ; ; MACRO CALLS ; .MCALL EXIT$S,QIOW$,ALUN$S,DIR$,GLUN$S,QIOW$S ; FCS MACROS .MCALL GCML$,GCMLB$,CSI$,CSI$1,CSI$2,CSI$ND .MCALL HMBOF$,FHDOF$ .MCALL NMBLK$,FDOP$A,FDRC$A,FDBK$A,OPEN$R,READ$ .MCALL OPEN$W,FINIT$,FDBDF$,FSRSZ$,CLOSE$,WRITE$,WAIT$ HMBOF$ DEF$L ;LOCALLY DEFINE HOME BLOCK OFFSETS FHDOF$ DEF$L ;LOCALLY DEFINE FILE HEADER OFFSETS ; CSI$ ;DEFINE CSI CONTROL BLOCK OFFSETS CSIBLK: .BLKB C.SIZE ;ALLOCATE THE CSI CONTROL BLOCK .EVEN ; GCLBLK: GCMLB$ 4,UND,,5 ;ALLOCATE AND INITIALIZE GCML CONTROL BLOCK ;COMMAND PROMPT IS INITIALIZED TO UND> ;LUN 5 IS USED FOR TI: FSRSZ$ 1 ;1 FILE OPEN FOR GCML .PAGE ; ; DATA ; OWNER: .WORD 0 ;UIC OF OWNER STBK: .BLKW 5 ;FCS STATISTICS BLOCK IOSB: .BLKW 2 ; ; BLOCK IO BUFFERS ; HDR: .BLKB 512. ;INDEX HEADER BLOCK BUFFER DATA: .BLKB 512. ;DELETED FILE BLOCK BUFFER ;+ ; UNDEL - UNDELETE A FILE RECENTLY DELETED ; ; INPUTS: ; COMMANDLINE FORMAT: "UND>DO:=DI:[grp,mbr]" ; ; Where DO: is the output device, DI: is the input device ; and the output and input devices are not the same. ; ; OUTPUTS: ; Places the undeleted file into the users current UIC on ; the output device. ; LUNS: ; LUN 1 = OUTPUT DEVICE ; LUN 2 = INPUT DEVICE ; LUN 3 = INPUT DEVICE ; LUN 4 = INPUT DEVICE ; LUN 5 = TI: FOR COMMAND LINE ; ; ;- START: FINIT$ ;INITIALIZE FSC UNDEL: CALL GETCML ;GET THE COMMAND LINE CALL INDEX ;LOOK THROUGH THE INDEX FILE FOR DELETED FILES BR UNDEL .PAGE .SBTTL COMMAND LINE PROCCESSING ; ; EQUATED SYMBOLS ; OUTSPC = ;OUTPUT SPECIFICATIONS INSPC = ;INPUT SPECIFICATIONS ;+ ; GETCML - GET THE COMMAND LINE AND PARSE IT ; ; INPUTS: ; NONE ; OUTPUTS: ; NONE ; ; EFFECTS: ; LUNS ASSIGNED TO PROPER DEVICES ;- GETCML: GCML$ #GCLBLK ;TRY TO GET THE COMMAND LINE BCC 10$ ;IF CC, GET WAS SUCCESSFUL EXIT$S ;IF CS, PROBABLY EOF SO GET OUT 10$: MOV R0,R1 ;R1 = #GCLBLK CSI$1 #CSIBLK,G.CMLD+2(1),G.CMLD(1) BCS SYNERR ;IF CS, SYNTAX ERROR CSI$2 ,OUTPUT ;PARSE THE OUTPUT SPEC BCS SYNERR ;IF CS, OUTPUT PARSE ERROR BITB #OUTSPC,C.STAT(0) ;DID OUTPUT HAVE PROPER SPECS BNE SYNERR ;IF NE, INCORRECT OUTPUT SPECS BITB #CS.DVF,C.STAT(0) ;SPEC MUST HAVE DEVICE BEQ SYNERR ;IF EQ, NO OUTPUT DEVICE CALL DEVICE ;GET DEVICE NAME FROM CSI BLOCK ALUN$S #1,R3,R4 ;ASSIGN LUN 1 TO OUTPUT DEVICE ; CSI$2 #CSIBLK,INPUT ;PARSE THE INPUT SPEC BCS SYNERR ;IF CS, INPUT PARSE ERROR BITB #INSPC,C.STAT(0) ;DID INPUT HAVE PROPER SPECS? BNE SYNERR ;IF NE, INCORRECT INPUT SPECS BITB #CS.DVF,C.STAT(0) ;SPEC MUST HAVE DEVICE BEQ SYNERR ;IF EQ, NO INPUT DEVICE MOV #CSIBLK+C.DIRD,R2 ;INPUT UIC STRING ADDRESS MOV #OWNER,R3 ;DIRECTORY BUFFER ADDRESS CALL .ASCPP ;CONVERT UIC STRING CALL DEVICE ;INPUT DEVICE FROM CSI BLOCK ALUN$S #2,R3,R4 ;ASSIGN LUN 2 ALUN$S #3,R3,R4 ;ASSIGN LUN 3 TO INPUT DEVICE ALUN$S #4,R3,R4 ;ASSIGN LUN 4 CALL DEVCHK ;CHECK IF DEVICES ARE SAME BCS DEVERR ;IF CS, DEVICES ARE THE SAME RETURN SYNERR: MOV #SYNMSG,R1 CALL WRITE JMP GETCML DEVERR: MOV #DEVMSG,R1 CALL WRITE JMP GETCML .PAGE ; ; LOCAL DATA ; .EVEN HOLD: .BLKW 1 ;BUFFER FOR DEVICE NAME ;+ ; DEVICE - GET DEVICE NAME FOR ALUN DIRECTIVE ; ; INPUTS: ; DEVICE DESCRIPTOR IN CSI BLOCK ; OUTPUTS: ; R3 = DEVICE NAME (IN ASCII) ; R4 = UNIT NUMBER (IN BINARY) ;- DEVICE: MOV #CSIBLK+C.DEVD,R0 ;DEVICE NAME DESCRIPTOR MOV (R0)+,R1 ;LENGTH OF DEVICE NAME STRING MOV (R0),R0 ;POINTER TO STRING MOVB (R0)+,HOLD ;GET LOW CHARACTER MOVB (R0)+,HOLD+1 ;GET HIGH CHARACTER MOV HOLD,R3 ;R3 = DEVICE NAME CLR R4 ;UNIT DEFAULTS TO 0 SUB #2,R1 ;DEVICE TAKES 2 CHARACTERS BEQ 20$ ;IF EQ, DONE (WE'LL LET SYNTAX SLIDE) 10$: CMPB (R0),#': ;IF :, DONE BEQ 20$ MOVB (R0)+,R2 ;GET DIGIT SUB #'0,R2 ;CONVERT FROM ASCII .REPT 3 ASL R4 ;BUMP HIGHER DIGITS BY 8 (OCTAL) .ENDR ADD R2,R4 ;ADD LOW DIGIT DEC R1 ;DECREASE LENGTH BGT 10$ ;IF GT, STILL CHARACTERS LEFT 20$: RETURN ;+ ; DEVCHK - CHECK FOR SAME DEVICE ; ; INPUTS: ; NONE ; OUTPUTS: ; CARRY SET IF DEVICES SAME ;- DEVCHK: MOV #HDR,R0 ;USE HDR AS GLUN BUFFER GLUN$S #2,R0 ;GET INPUT INFO MOV #DATA,R1 ;USE DATA AS OTHER GLUN BUFFER GLUN$S #1,R1 ;GET OUTPUT INFO CMP G.LUNA(R0),G.LUNA(R1) ;COMPARE DEVICES BNE 10$ ;IF NE, DEVICES ARE DIFFERENT CMPB G.LUNU(R0),G.LUNU(R1) ;CHECK UNITS IF SAME TYPE DEVICE BNE 10$ ;IF NE, UNITS ARE DIFFERENT SEC ;SET THE CARRY, INPUT = OUTPUT RETURN 10$: CLC RETURN .PAGE .SBTTL INDEX FILE SEARCH ; ; FDB'S ; ; THE OUTPUT FILE OUTFDB: FDBDF$ FDRC$A FD.RWM ;BLOCK I/O OPERATIONS FDBK$A DATA,512.,,,IOSB FDOP$A 1,,DFNB,FO.WRT DFNB: NMBLK$ FILE,DAT,0 ;DEFAULT FILE NAME = FILE.DAT;0 ; ; THE INPUT INDEX FILE ; INXFDB: FDBDF$ FDRC$A FD.RWM ;BLOCK I/O OPERATIONS FDBK$A HDR,512.,,,IOSB FDOP$A 2,INDX,,FO.RD INDX: .WORD 0,0 ;DATA SET DESCRIPTOR BLOCK .WORD INXULN,INXUIC .WORD INXFLN,INXFIL INXUIC: .ASCII /[0,0]/ INXULN=.-INXUIC INXFIL: .ASCII /INDEXF.SYS/ INXFLN=.-INXFIL .EVEN ; ; THE BITMAP FILE ; BMPFDB: FDBDF$ FDRC$A FD.RWM ;BLOCK I/O OPERATIONS FDBK$A DATA,512.,,,IOSB FDOP$A 3,BITM,,FO.RD BITM: .WORD 0,0 ;DATA SET DESCRIPTOR BLOCK .WORD INXULN,INXUIC .WORD BITFLN,BITFIL BITFIL: .ASCII /BITMAP.SYS/ BITFLN=.-BITFIL .EVEN .PAGE ;+ ; INDEX - SEARCH THE INDEX FILE ; ; INPUTS: ; LUNS 3,4 ASSIGNED TO INPUT DEVICE ; OWNER = UIC ON INPUT DEVICE ; ; OUTPUTS: ; NONE ;- INDEX: MOV #INXFDB,R0 CALL OPENFL ;OPEN THE INDEX FILE BCC 10$ MOV #INXFDB+F.ERR,ARGBLK ;ERROR CODE FOR MESSAGE MOV #INXMSG,R1 CALL WRITE RETURN 10$: MOV #BMPFDB,R0 CALL OPENFL ;OPEN THE BITMAP FILE BCC 15$ MOV #BMPFDB+F.ERR,ARGBLK ;ERROR CODE FOR MESSAGE MOV #BITMSG,R1 CALL WRITE BR 60$ ; ; DEFAULTS TO SEQUENTIAL READS THROUGH INDEX FILE ; 15$: READ$ #INXFDB ;READ THE BOOT STRAP BLOCK WAIT$ R0 READ$ R0 ;READ THE HOME BLOCK WAIT$ R0 MOV HDR+H.IBSZ,R5 ;SAVE THE INDEX-BIT-MAP SIZE 20$: READ$ R0 ;READ THROUGH THE INDEX-BIT-MAP WAIT$ R0 SOB R5,20$ ADD #5,F.VBN+2(0) ;SKIP 5 BLOCKS TO 1ST USER FILE HEADER ADC F.VBN(0) 30$: READ$ #INXFDB ;READ INDEX FILE HEADER BCS 50$ ;IF ERROR, ASSUME EOF WAIT$ R0 BCS 50$ ;IF CS, FILE ERROR , CLOSE FILE CMP HDR+H.IDOF,#27027 ;ALL HEADER IDS SET TO 27027 WHEN CREATED BNE 40$ ;IF NE, BLOCK DOESN'T CONTAIN FILE HEADER TST HDR+H.FNUM ;TEST IF THIS FILE HEADER WAS DELETED BNE 40$ ;IF NE, THIS HEADER IS STILL IN USE CALL CHKFIL ;CHECK IF PROPER UIC BCS 40$ ;IF CS, NOT RIGHT FILE CALL COPY ;TRY TO RECOVER THIS FILE BCS 50$ ;IF CS, ERROR IN COPYING FILE 40$: BR 30$ ;CONTINUE UNTIL EOF 50$: CLOSE$ #BMPFDB ;CLOSE THE BITMAP FILE 60$: CLOSE$ #INXFDB ;CLOSE INDEX FILE RETURN ;+ ; OPENFL - OPEN FILE WITH STATISTICS BLOCK ; ; INPUTS: ; R0 = ADDRESS OF FDB ; OUTPUTS: ; CARRY SET IF FILE OPEN ERROR ;- OPENFL: MOV #STBK,F.STBK(0) ;SPECIFY STATISTICS BLOCK OPEN$R R0 ;OPEN THE FILE MOV STBK+4,F.HIBK(0) ;GET HIGHEST VBN FROM STBK MOV STBK+6,F.HIBK+2(0) ;GET LOW WORD ADD #1,STBK+6 ;SET EOF VBN TO HIGH BLOCK + 1 ADC STBK+4 MOV STBK+4,F.EFBK(0) ;GET EOF VBN MOV STBK+6,F.EFBK+2(0) ;GET LOW WORD RETURN .PAGE ;+ ; CHKFIL - CHECK IF FILE IS TO BE RECOVERED ; ; INPUTS: ; NONE ; OUTPUTS: ; CARRY CLEAR - RECOVER THE FILE ; CARRY SET - WRONG FILE ;- CHKFIL: CMP HDR+H.FOWN,OWNER ;CORRECT UIC?? BEQ 10$ ;IF EQ, FILE HAS CORRECT UIC SEC ; ELSE SET CARRY AND RETURN RETURN 10$: MOV #5,R5 MOVB HDR+H.IDOF,R0 ;OFFSET TO ID AREA IN WORDS ASL R0 ;CONVERT TO BYTES ADD #HDR+I.FNAM,R0 ;ADDRESS TO FILE NAME IN RADIX MOV #ARGBLK,R1 20$: MOV (R0)+,(R1)+ ;MOVE FILE NAME TO ARGBLK SOB R5,20$ MOV #FILMSG,R1 ;WRITE FILENAME BEFORE RECOVERING CALL WRITE CLC RETURN ;+ ; COPY - OPEN THE OUTPUT FILE TO COPY INTO ; ; INPUTS: ; ACCEPTABLE FILE HEADER HAS BEEN FOUND ; ; OUTPUTS: ; CC = SUCCESSFUL COPY ; CS = ERROR WITH OUTPUT FILE ; ; FILE CREATED ON OUTPUT DEVICE IN USERS UIC ; ;- COPY: MOV #HDR,R1 ;DELETED FILE HEADER ON INPUT DEVICE MOVB H.IDOF(R1),R2 ;HEADER OFFSET TO ID AREA MOVB H.MPOF(R1),R3 ;HEADER OFFSET TO MAPPING AREA ASL R2 ;CONVERT OFFSETS FROM WORDS TO BYTES ASL R3 ADD R1,R2 ;R2 = ADDRESS OF ID AREA ADD R1,R3 ;R3 = ADDRESS OF MAPPING MOV #DFNB+N.FNAM,R4 ;R4 = ADDRESS FOR FILENAME IN DEFAULT BLK MOV #5,R5 ;5 WORDS FOR DFNB 10$: MOV (R2)+,(R4)+ ;GET THE DELETED FILE NAME SOB R5,10$ OPEN$W #OUTFDB ;OPEN THE OUTPUT FILE FOR WRITING BCC 20$ ;IF CC, FILE OPENED OK MOV #OUTFDB+F.ERR,ARGBLK ;GET ERROR CODE FOR MESSAGE MOV #OUTMSG,R1 ;SEND ERROR MESSAGE TO TERMINAL CALL WRITE SEC RETURN 20$: CALL COPYBK ;GO COPY THE FILE BCS 40$ ;IF CS, UNRECOVERABLE ERROR IN COPYBK MOV #HDR+H.UFAT,R1 ;R1 = ADDRESS OF USER ATTRIBUTES FOR FILE MOV #OUTFDB,R0 ;R0 = DESTINATION OF ATT. MOV #7,R5 ;MOVE 7 WORDS OF ATT. 30$: MOV (R1)+,(R0)+ ;MOVE ATTRIBUTES TO OUTPUT FDB SOB R5,30$ CLOSE$ #OUTFDB ;CLOSE THE OUTPUT FILE CLC ;CLEAR CARRY, INDICATE SUCCESS 40$: RETURN .PAGE ; ; LOCAL MACROS ; RDQIO: QIOW$ IO.RLB,4,4,,IOSB,, ; ;+ ; COPYBK - COPY THE FILE BLOCKS POINTED TO BY THE MAP AREA ; ; INPUTS: ; R3 = ADDRESS OF MAPPING AREA IN HEADER ; ; OUTPUTS: ; NONE ;- COPYBK: CLR R5 BISB M.USE(R3),R5 ;WORDS OF RETRIEVAL POINTERS ASL R5 ;BYTES OF RETRIEVAL POINTERS ADD #M.RTRV,R3 ;ADDRESS OF FIRST POINTER 10$: TST R5 ;TEST IF DONE BLE 50$ ;IF LE, DONE WITH FILE ; ; ASSUME SIZE OF CONTIGUOUS BLOCK COUNT - 1 = 1 BYTE (HIGH BYTE OF 1ST WORD) ; AND SIZE OF LBN FIELD IS 3 BYTES ; CLR R1 BISB (R3)+,R1 ;R1 = HIGH WORD OF LBN CLR R4 BISB (R3)+,R4 ;R4 = CONTIGUOUS BLOCK COUNT - 1 INC R4 ;0 ... -> 1 ... MOV (R3)+,R2 ;R2 = LOW WORD OF LBN 20$: CALL BLKCHK ;CHECK IF BLOCK REUSED BCS 35$ ;IF CS, ERROR IN BLKCHK MOV #RDQIO,R0 ;ADDRESS OF READ QIO MOV R1,Q.IOPL+6(0) ;HIGH WORD OF LBN MOV R2,Q.IOPL+10(0) ;LOW WORD OF LBN DIR$ R0 ;READ ONE BLOCK OF FILE BY LBN WRITE$ #OUTFDB ;WRITE THE BLOCKTO OUTPUT DEVICE BCS 30$ WAIT$ R0 BCC 40$ 30$: MOV #OUTFDB+F.ERR,ARGBLK ;GET ERROR CODE MOV #OUTMSG,R1 CALL WRITE SEC 35$: RETURN 40$: ADD #1,R2 ;NEXT CONTIGUOUS BLOCK ADCB R1 SOB R4,20$ ;READ ALL CONTIGUOUS BLOCKS SUB #4,R5 ;R5 = NEW BYTES OF RETREIVAL POINTERS BR 10$ 50$: CLC RETURN .PAGE ; ; LOCAL DATA ; BITCHK: .BYTE 1 ;BIT CHECK TABLE FOR BIT MAP .BYTE 2 .BYTE 4 .BYTE 10 .BYTE 20 .BYTE 40 .BYTE 100 .BYTE 200 .EVEN ;+ ; BLKCHK - CHECK IF THE BLOCK HAS BEEN REUSED ; ; INPUTS: ; R1,R2 = DOUBLE PRECISION LOGICAL BLOCK NUMBER ; ; OUTPUTS: ; NONE ; ; EFFECTS: ; WARNING WRITTEN TO TERMINAL IF BLOCK REUSED ;- BLKCHK: CALL $SAVAL ;SAVE THE REGISTERS MOV R2,R3 MOV R1,R2 ;R1,R2 => R2,R3 MOV R3,R1 BIC #177770,R1 ;R1 = BITMAP BIT NUMBER IN BYTE .REPT 3 ASR R2 ;DOUBLE PREC. DIVIDE BY 8. ROR R3 ;(BITMAP IS 8 BLOCKS PER BYTE) .ENDR ; ; COMPUTE VBN IN BITMAP.SYS, (512. * 8.) = DEVICE BLOCKS PER BITMAP BLOCK ; DIV #1000,R2 ;DIVIDE R2,R3 BY 1000 ;R2 = VBN OF BITMAP FILE ;R3 = BYTE ADDRESS INTO BITMAP BLOCK ADD #2,R2 ;MAP STARTS AT SECOND BLOCK MOV R2,BMPFDB+F.BKVB+2 ;INSERT VBN IN FDB CLR BMPFDB+F.BKVB READ$ #BMPFDB ;READ BLOCK OF BITMAP BCC 10$ CMPB BMPFDB+F.ERR,#IE.EOF ;END OF FILE? BNE 5$ ;IF NE, PRINT ERROR MESSAGE CMP IOSB+2,R3 ;WERE ENOUGH BYTES READ IN? BGT 10$ ;IF GE,ENOUGH BYTES WERE READ 5$: MOV #BMPFDB+F.ERR,ARGBLK ;ERROR CODE FOR MESSAGE MOV #BITMSG,R1 CALL WRITE SEC RETURN 10$: WAIT$ R0 BCS 5$ BITB BITCHK(1),DATA(3) BNE 20$ ;IF NE, BLOCK STILL UNUSED MOV #WARN,R1 ;SEND WARNING OF CORRUPTION CALL WRITE 20$: CLC RETURN .PAGE .SBTTL TERMINAL IO ; ; TERMINAL MESSAGES ; MSG: .ASCIZ /UND -- / SYNMSG: .ASCIZ /%N%ISyntax Error, "UND>DOut:=DIn:[grp,mbr]"/ DEVMSG: .ASCIZ /%N%IDevices must be distinct/ FILMSG: .ASCIZ /%N%IRecovering %X/ WARN: .ASCIZ /%N%I*** Warning %X possibly corrupted ***/ OUTMSG: .ASCIZ /%N%I*** Output File error, error code = %BOctal ***/ INXMSG: .ASCIZ /%N%I*** Input Index File error = %BOctal ***/ BITMSG: .ASCIZ /%N%I*** Input Bitmap File error = %BOctal ***/ .EVEN ; ; LOCAL DATA ; PREARG: .WORD MSG ARGBLK: .BLKW 6 ;ARGUMENT BLOCK FOR EDMSG ;+ ; WRITE - SEND MESSAGE TO TI: ; ; INPUTS: ; R1 = INPUT STRING ; OUTPUTS: ; NONE ;- WRITE: MOV R0,-(SP) MOV R1,-(SP) MOV R2,-(SP) MOV #PREARG,R2 ;ARGUMENT BLOCK FOR EDMSG MOV #DATA,R0 ;OUTPUT BUFFER FOR EDMSG CALL $EDMSG QIOW$S #IO.WAL,#5,#5,,,,<#DATA,R1> MOV (SP)+,R2 MOV (SP)+,R1 MOV (SP)+,R0 RETURN .END START