<<< ISVNET::DISK$OTHER:[NOTES$LIBRARY]VMS.NOTE;1 >>> -< VMS Operating System >- ================================================================================ Note 307.2 Undelete utility? 2 of 4 XLIB::SCHAFER "Mark Schafer, ISV Tech. Support" 283 lines 16-DEC-1991 16:33 -< undelete.for >- -------------------------------------------------------------------------------- PROGRAM UNDELETE !!-** UNDELETE A PROGRAM ON ODS-2 VAX DISK **-!! IMPLICIT NONE ! MAKE SURE ALL VARIABLES ARE DEFINED ! REGULAR PROGRAM VARIABLES CHARACTER*86 FILNAM,RECNAM,INFLNM*86,DCLCOM*135,DEVICE*10,ANSWER*1 INTEGER*4 SEARCH,HEADRS,FILVER,LENCHK,NOSP,VBN,IOST ! HOME BLOCK BYTE BYTE4(4),BYTE8(8),BYTE13(13),STRUCT INTEGER*2 MAPVBN,MAPSIZ INTEGER*4 MAXFIL ! FILE HEADER DATA BYTE RECORD(512) INTEGER*2 INFID(3),EXTFID(3),BAKFID(3),FILID(3),CHKSUM INTEGER*4 FILCHR 0 EQUIVALENCE ( FILID(1), RECORD(9) ), ! MAKE IT EASY TO READ DATA 1 (EXTFID(1), RECORD(15)), ! FROM THE HEADER RECORD. 2 ( FILCHR, RECORD(53)), 3 (BAKFID(1), RECORD(67)), 4 ( CHKSUM,RECORD(511)) ! GET DEVICE, AND FILE NAME OR FILE ID. TYPE *,' ' TYPE *,' THIS PROGRAM RECOVERS DELETED FILES FROM A FILES-11 ODS-2 DISK' TYPE *,' ' 10 WRITE(*,'(4X,''DEVICE --> '',$)') READ(*,'(A)')DEVICE CALL STR$UPCASE(DEVICE,DEVICE) DEVICE=DEVICE(1:INDEX(DEVICE,':')) IF(NOSP(DEVICE).EQ.0) THEN TYPE *,'ENTER A DEVICE WITH A '':'' ON THE END.' GOTO 10 ENDIF 20 TYPE *,' DO YOU WANT TO ENTER --> 0) FILE ID OR,' WRITE(*,'(4X,'' --> 1) FILE NAME (0/1) '',$)') READ (*,'(I1)')SEARCH IF (SEARCH.EQ.0) THEN WRITE(*,'(4X,''WHAT IS THE FILE ID XXXX,YYY,ZZZ ? '',$)') READ(*,'(I,I,I)')INFID(1),INFID(2),INFID(3) IF (INFID(1).EQ.4.AND.INFID(2).EQ.4.AND.INFID(3).EQ.0) THEN TYPE *,' ' TYPE *,'THAT IS THE MASTER FILE DIRECTORY. DON''T MESS WITH IT.' GOTO 160 ENDIF ! WE NEED TO DO THIS OR GET A LOCKED TARGET STREAM IN FINDIR IF (INFID(1).LE.0) THEN TYPE *,' ' TYPE *,'LOWEST RECORD # = 1.' GOTO 160 ENDIF ELSE WRITE(*,'(4X,''FILE NAME --> '',$)') READ(*,'(A)')FILNAM CALL STR$UPCASE(FILNAM,FILNAM) FILVER=INDEX(FILNAM,';') ENDIF TYPE *,' ' 0 OPEN(1,FILE=DEVICE(1:NOSP(DEVICE))//'[000000]INDEXF.SYS',SHARED, 1 ACCESS='DIRECT',FORM='UNFORMATTED',RECORDTYPE='FIXED',STATUS='OLD') ! READ THE HOME BLOCK TO GET THE ODS STRUCTURE, THE MAP AREA, THE MAX FILES, ! AND THE MAP SIZE SO WE CAN GET BLOCKS WICH HEADERS START AFTER. READ(1,REC=2) BYTE13,STRUCT,BYTE8,MAPVBN,BYTE4,MAXFIL,MAPSIZ IF (STRUCT.NE.2) THEN TYPE *,'THIS IS NOT A FILES-11 ODS-2 DISK.' GOTO 150 ENDIF HEADRS=MAPVBN+MAPSIZ IF (SEARCH.EQ.0) THEN ! IF LOOKING BY FILE ID VBN=INFID(1)+HEADRS-1 READ(1,REC=(INFID(1)+HEADRS-1),ERR=127) RECORD IF (FILID(2).NE.INFID(2)) GOTO 140 ! HAS HEADER BEEN REUSED? CALL GETNAM(RECORD,RECNAM) ! GET FNAM FOR THIS REC GOTO 110 ENDIF ! SEARCH ALL HEADERS UNTIL ONE WITH THE SAME FILE NAME AND VERSION IS FOUND VBN=HEADRS 100 VBN=VBN+1 READ(1,REC=VBN,ERR=130,IOSTAT=IOST) RECORD CALL GETNAM(RECORD,RECNAM) LENCHK=INDEX(RECNAM,';')-1 IF (FILVER.GT.0.OR.LENCHK.LE.0) LENCHK=86 IF (FILNAM.NE.RECNAM(1:LENCHK)) GOTO 100 110 0 CALL FINDIR(BAKFID,HEADRS,DEVICE(1:NOSP(DEVICE)), ! GET FULL DIR SPEC 1 RECNAM(1:NOSP(RECNAM)),INFLNM) IF (CHKSUM.NE.0.AND.FILID(1).NE.0) THEN INFLNM=INFLNM(1:NOSP(INFLNM))//' --> NOT DELETED' ! FILE STILL ALIVE WRITE(*,'(X,A)')INFLNM IF (SEARCH.EQ.1) GOTO 100 ! IF LOOKING BY FNAM ! LOOK FOR OTHERS TYPE *,'THAT FILE ID IS ALIVE AND DOING WELL.' GOTO 150 ENDIF INFLNM=INFLNM(1:NOSP(INFLNM))//' --> RECOVER? (Y/N)' WRITE(*,'(X,A,$)') INFLNM(1:NOSP(INFLNM)+1) READ (*,'(A)') ANSWER IF (ANSWER.NE.'Y'.AND.ANSWER.NE.'y') THEN IF (SEARCH.EQ.1) GOTO 100 TYPE *,'O.K. FILE WILL STAY DELETED.' GOTO 150 ENDIF ! FINALLY FOUND A FILE TO BRING BACK FROM THE DEAD. PUT IN THE FILID ! AND CHKSUM, CLEAR DELETE BIT IN FILCHR AND MAPPING FILID(1)=VBN-HEADRS+1 ! FILE ID # IS CURRENTLY 0 FILCHR=FILCHR.AND.'FFFF7FFF'X ! SAY HEADER IS NOW USED ! GET THE CHANGED CHKSUM AND REPLACE THAT TOO CALL CHECKR(RECORD) ! CHECKSUM THE NEW HEADER TYPE *,' ' TYPE *,'REWRITING HEADER AND CHECKSUM.' WRITE(1,REC=(FILID(1)+HEADRS-1)) RECORD ! REWRITE THE NEW HEADER 120 IF (EXTFID(1).EQ.0) GOTO 125 ! IF NO MORE EXT HDRS- END INFID(1)=EXTFID(1) ! INFID IS CUR EXT HDR # READ(1,REC=INFID(1)+HEADRS-1) RECORD ! READ EXT HDR CALL GETNAM(RECORD,RECNAM) ! GET FNAM IN EXT HDR IF (FILNAM.NE.RECNAM(1:INDEX(RECNAM,';')-1)) THEN TYPE *,'INDEXF.SYS IS CORRUPT - BAD FILENAME IN EXTENSION HEADER' GOTO 125 ! ALREADY REWRITTEN ORIGINAL HEADER - TRY TO GET SOME DATA ENDIF IF (CHKSUM.NE.0.AND.FILID(1).NE.0) THEN TYPE *,'INDEXF.SYS IS CORRUPT - EXTENSION HEADER IS NOT DELETED.' GOTO 125 ! ALREADY REWRITTEN ORIGINAL HEADER - TRY TO GET SOME DATA ENDIF FILID(1)=INFID(1) ! PUT IN GOOD FID # FILCHR=FILCHR.AND.'FFFF7FFF'X ! SAY HEADER IS USED CALL CHECKR(RECORD) ! CHECKSUM RECORD WRITE(1,REC=(FILID(1)+HEADRS-1)) RECORD ! REWRITE RECORD GOTO 120 ! LOOK FOR MORE EXT HDRS 125 CLOSE(1) ! PROBABLY DON'T NEED TO HAVE FILE CLOSED BEFORE ANALYZE ! BUT YOU CAN'T EVER BE TO SURE ABOUT THESE THINGS TYPE *,' ' TYPE *,'FILE HEADER IS UPDATED. BITMAP AND INDEX UPDATE IN PROGRESS.' TYPE *,' ' TYPE *,'PLEASE BE PATIENT, LOTS OF I/O.' DCLCOM='$ ANALYZE/DISK_STRUCTURE/REPAIR '//DEVICE(1:NOSP(DEVICE)) ! WE HAVE TO DO TWO ANALYZE/DISK_STRUCTURE/REPAIRs. THE FIRST ONE SEES ! THAT THE FILE HAS NO DIRECTORY. IT PUTS IT IN [SYSLOST] AND MARKS THE ! BITS AS USED IN THE BITMAP. IT TAKES CARE OF THE CASE OF MULTIPLY ALLOCATED ! CLUSTERS, DISK QUOTAS AND ALL OTHER PROBLEMS. THE NEXT TIME IT IS ANALYZEd, ! IT FIXES THE FILES INCORRECT DIRECTORY BACKLINK TO [000000]SYSLOST.DIR CALL LIB$SET_LOGICAL('SYS$ERROR','NL:') ! PUT ALL THOSE NASTY ERROR CALL LIB$SET_LOGICAL('SYS$OUTPUT','NL:') ! MESSAGES OUT TO THE INFINITE CALL LIB$SPAWN(DCLCOM(1:NOSP(DCLCOM))) ! STORAGE CAPABILITY NULL PORT CALL LIB$DELETE_LOGICAL('SYS$OUTPUT') ! WRITE ONE LINE TYPE *,' ' TYPE *,'CURRENTLY CORRECTING DIRECTORY BACKLINK TO [SYSLOST].' CALL LIB$SET_LOGICAL('SYS$OUTPUT','NL:') ! THEN TURN OFF OUTPUT CALL LIB$SPAWN(DCLCOM(1:NOSP(DCLCOM))) ! DO ANALYZE AGAIN CALL LIB$DELETE_LOGICAL('SYS$OUTPUT') ! BRING SYSTEM OUTPUT BACK CALL LIB$DELETE_LOGICAL('SYS$ERROR') ! TO NORMAL AGAIN 0 DCLCOM='$ DIRECTORY/COLUMNS=1/SIZE=ALL '//DEVICE(1:NOSP(DEVICE))// 1 '[SYSLOST]'//RECNAM(1:NOSP(RECNAM)) CALL LIB$SPAWN(DCLCOM(1:NOSP(DCLCOM))) ! SHOW THAT THE FILE IS THERE GOTO 160 ! AND THAT'S ALL FOLKS. 127 TYPE *,'THERE AREN''T THAT MANY HEADERS.' GOTO 150 130 IF (IOST.NE.36) THEN ! PRETTY TRICKY, USING TYPE *,'ERROR READING INDEXF.SYS' ! THE SAME 'HEADER REUSED' GOTO 150 ! LINE FOR ERRORS IN TWO ENDIF ! DIFFERENT PLACES. 140 TYPE *,'SORRY, YOUR HEADER HAS BEEN REUSED.' 150 CLOSE(1) 160 END ! SUBROUTINE GIVEN A FILE ID, WILL GET THE FILE'S FULL DIRECTORY SPECIFICATION SUBROUTINE FINDIR(RDRFID,HEADRS,DEVICE,RRCNAM,WRCNAM) IMPLICIT NONE BYTE RECORD(512) INTEGER*2 FID(3),RDRFID(3),DRFID(3),BAKFID(3) INTEGER*4 HEADRS,NUMDIR,NOSP CHARACTER*10 DEVICE,WRCNAM*86,RRCNAM*86,TEMP*135 EQUIVALENCE (FID(1),RECORD(9)) EQUIVALENCE (BAKFID(1),RECORD(67)) DRFID(1)=RDRFID(1) DRFID(2)=RDRFID(2) DRFID(3)=RDRFID(3) WRCNAM=RRCNAM TEMP='A' NUMDIR=1 0 OPEN(2,FILE=DEVICE(1:NOSP(DEVICE))//'[000000]INDEXF.SYS',SHARED,READONLY, 1 FORM='UNFORMATTED',RECORDTYPE='FIXED',STATUS='OLD',ACCESS='DIRECT') 100 READ(2,REC=(DRFID(1)+HEADRS-1)) RECORD ! READ THIS RECORD TEMP=WRCNAM CALL GETNAM(RECORD,WRCNAM) ! GET THE FNAM IF (NUMDIR.GE.1) THEN !- PATCH THE NAMES WRCNAM=WRCNAM(1:INDEX(WRCNAM,'.')) ! ALL TOGETHER SO IF (NUMDIR.EQ.1) WRCNAM=WRCNAM(1:NOSP(WRCNAM)-1)//']' ! END PRODUCT IS NUMDIR=NUMDIR+1 !- A FUL DIR SPEC ENDIF WRCNAM=WRCNAM(1:NOSP(WRCNAM))//TEMP(1:NOSP(TEMP)) TEMP=' ' DRFID(1)=BAKFID(1) DRFID(2)=BAKFID(2) ! KEEP ON RECURSIVELY CALLING YOURSELF DRFID(3)=BAKFID(3) ! WITH BACKLINK FILE_IDS TO EACH DIR IF (.NOT.(DRFID(1).EQ.4.AND.DRFID(2).EQ.4.AND.DRFID(3).EQ.0)) GOTO 100 TEMP=WRCNAM !^^ IF IT IS THE MFD STOP LOOKING CAUSE WRCNAM='[000000.' !^^ IT POINTS TOWARDS ITSELF IF (NUMDIR.EQ.1) THEN WRCNAM=WRCNAM(1:NOSP(WRCNAM)-1)//']'//TEMP(1:NOSP(TEMP)) ELSE WRCNAM=WRCNAM(1:NOSP(WRCNAM))//TEMP(1:NOSP(TEMP)) ENDIF CLOSE(2) RETURN END ! SUBROUTINE THAT GIVEN A HEADER RECORD, WILL EXTRACT THE REDUNDANT FILE NAME SUBROUTINE GETNAM(RECORD,RECNAM) IMPLICIT NONE BYTE RECORD(512) CHARACTER*86 RECNAM INTEGER*4 COUNT,IDOFF,MPOFF IDOFF=RECORD(1) IDOFF=IDOFF*2 MPOFF=RECORD(2) MPOFF=MPOFF*2 IF (IDOFF.LE.0.OR.IDOFF.GT.512) GOTO 100 DO COUNT=1,20 ! GET FIRST PART OF RECNAM(COUNT:COUNT)=CHAR(RECORD(IDOFF+COUNT)) ! FILE NAME IF (IDOFF+COUNT+1.GT.MPOFF) GOTO 100 ENDDO DO COUNT=21,86 ! GET OTHER PART LATER RECNAM(COUNT:COUNT)=CHAR(RECORD(IDOFF+COUNT+34))! ON IN IDENT AREA, IF IF (IDOFF+COUNT+35.GT.MPOFF) GOTO 100 ! FILENAME NEEDS IT ENDDO 100 RETURN END ! FUNCTION THAT GIVEN A STRING, RETURNS THE NON-SPACE-FILLED LENGTH OF STRING INTEGER FUNCTION NOSP(STRING) IMPLICIT NONE INTEGER*4 COUNT CHARACTER*(*) STRING DO COUNT=LEN(STRING),1,-1 IF (STRING(COUNT:COUNT).NE.' ') GOTO 100 ENDDO COUNT=0 100 NOSP=COUNT RETURN END