.MCALL .MODULE .MODULE BUPDR,VERSION=14,IDENT=NO,COMMENT= ; Copyright (c) 1998 by Mentec, Inc., Nashua, NH. ; All rights reserved ; ; This software is furnished under a license for use only on a ; single computer system and may be copied only with the ; inclusion of the above copyright notice. This software, or ; any other copies thereof, may not be provided or otherwise ; made available to any other person except for use on such ; system and to one who agrees to these license terms. Title ; to and ownership of the software shall at all times remain ; in Mentec, Inc. ; ; The information in this document is subject to change without ; notice and should not be construed as a commitment by Digital ; Equipment Corporation, or Mentec, Inc. ; ; Digital and Mentec assume no responsibility for the use or ; reliability of its software on equipment which is not supplied ; by Digital or Mentec, and listed in the Software Product ; Description. .SBTTL Disk-to-Disk Restore operation ; 23-Jun-89 RHH V5.5 work .ENABL LC .SBTTL Macros .MACRO ...... .ENDM .SBTTL Equates DEBUG = 0 ; Set to 1 for debug print statements ; New BUP (BUQ) format prefix block offsets BQ.S1LN =: 30 ; FIRST section length offset BQ.SILN =: 32 ; INTERMEDIATE section length offset BQ.SLLN =: 34 ; LAST section length offset BQ.TTLN =: 36 ; TOTAL saveset length offset BQ.NOSC =: 40 ; Number of sections offset BQ.SECN =: 42 ; THIS section number offset BQ.DATE =: 46 ; backup dateword BQ.TIME =: 50 ; Time-of-day area .SBTTL Macro References .MCALL .CLOSE .ENTER .FETCH .HERR .LOOKUP .PRINT .MCALL .PURGE .PVAL .READW .SERR SOB .SPFUN .MCALL .WAIT .WRITE .WRITW .SBTTL Data ;+ ; Pure and impure data for the RESTORE module ;- .PSECT XBADAT,D .NLIST BEX NONSAV: .WORD 0 ; saved NONAME flag bits NOBBL: .WORD 0 ; bad block count TEMP2: .WORD 0 OFFSET: .WORD 0 ; variable for VERIFY NPREFB: .WORD 0 ; No. of prefix blocks SEC1LN: .WORD 0 ; FIRST section length INTMLN: .WORD 0 ; INTERMEDIATE section length SECNLN: .WORD 0 ; LAST section length THISEC: .WORD 0 ; THIS section number DISKVN: .WORD 0 ; Disk's volume number EXPCTD::.WORD 0 ; Expected volume number SECTNO: .WORD 0 ; Section number REMAIN: .WORD 0 ; File restore block countdown VOLREM: .WORD 0 ; Remaining space on input section FRSTBL: .WORD 0 ; First block of section LASTBL: .WORD 0 ; Last block of section DATIME: .BLKW 5 ; backup saveset's date and time FORMAT: .BYTE 0 OUTINI: .BYTE 0 ; Output fetched/buffers calc flag TMPFLG: .BYTE 0 ; Temporary file written flag INOPEN: .BYTE 0 ; input-open flag XFORM: .BYTE 0 ; possible command line change done NSYMSG: .BYTE 0 ; One-time "No .SYS action" flag NOTBUQ: .ASCIZ /?BUP-E-Not a BACKUP saveset section/ OF: .ASCII / of /<200> .EVEN IDCHK: .RAD50 /BUP / .WORD 5. ; Expected BUP Major Version .IF NE DEBUG msg0: .asciz /*** First handler loaded ***/ msg1: .asciz /*** Back from VALID ***/ msg3: .asciz /*** Second handler loaded ***/ msg4: .asciz /*** Ready to restore ***/ msg33: .ascii /Blocks per buffer = /<200> BBRDVM: .ASCIZ \Detected RL01/2 or RK06/7 device.\ HBOPER: .ASCIZ /Beginning special home-block operation/ HBRT11: .ASCIZ /DECRT11A verified/ HBDONE: .ASCIZ /Special home-block operation done./ .ENDC .EVEN .LIST BEX .SBTTL Main code ;+ ; Description: ; ; RESTORE from DISK to DISK ; ;- .PSECT XBAC .ENABL LSB ; Start here before 1st volume XBACKO:: CALL SETRES ; Get device for prompt MOV INFIL,DEVSPC ; Get input device for NFS access MOV NHDRAD,R1 ; R1 -> available low memory ; If doing FILE restore, allocate buffers for IGTDIR and IGTENT BIT #,OPTACT ; doing /DEVICE or /FILE? BNE 5$ ; Then don't need IGTDIR stuff TSTB GIV.P ; /EXCLUDE? BPL 2$ INC IG.DEV+4 ; set IGTDIR's RETURN_ALL flag 2$: MOV R1,WKARED ; work area to use for IGTDIR MOV R1,WKAREA ; " " " " for IGTENT ADD #,R1 ; move next avail word past wk area MOV R1,IGDBUF ; directory buffer ADD #2000,R1 ; move next avail word past dir buf 5$: CALL FETHAN ; Go fetch INPUT handler MOV R0,NHDRAD ; Save address above handler ; The following commented lines are effectively done by loading ; this overlay. ; CLRB INOPEN ; No input is currently open ; CLRB OUTINI ; Output handler not fetched ; CLRB XFORM ; command transformation not done ; CLRB NSYMSG ; "No .SYS action" not printed ; CLR SECTNO ; Declare NO SECTIONS YET CLR NFTRAN ; Declare NO FILES TRANSFERRED CLR FNDBTS ; Clear the files-found bitmap MOV #1,EXPCTD ; Expected volume number MOV #1,VOLNUM ; " CLR REMAIN ; Trigger need for next file entry ; Begin here for each input volume 10$: CALL NEWINP ; Open new input volume. TSTB OUTINI ; 1st time through? BNE 20$ ; Branch if so. CALL PREPOU ; One-time prepare for output ; Determine whether we are continuing with a new input volume, or ; just starting now. Open output file/volume if necessary. 20$: TST REMAIN ; Ready for (another) input file? BNE 110$ ; Branch if continuing a previous one. ; Beginning a new restore BIT #XI$OPT,OPTACT ; Starting a DEVICE restore? BNE 80$ ; Yes, do that below. BIT #XF$OPT,OPTACT ; /RESTORE/FILE specified? BEQ 30$ ; branch if not MOV TOTLLN,REMAIN ; Old BUP file or device size MOV TOTLLN,FILSIZ ; BR 50$ ; go do it. ...... 30$: CALL NEXFIL ; Set up for an input file. BCC 50$ ; Branch if there is one. 40$: JMP 230$ ; Otherwise, finish up. ...... 50$: CMP THISEC,EXPCTD ; Is the correct section there? BEQ 60$ ; Branch if so. ; The next file entry found in the temporary container directory is ; on a subsequent input volume. MOV INBLK,-(SP) ; (INBLK already calculated-save it.) MOV VOLREM,-(SP) ; same with VOLREM .CLOSE INCHAN CLRB INOPEN CALL NEWINP ; ask for another input volume. MOV (SP)+,VOLREM ; recover VOLREM MOV (SP)+,INBLK ; recover the pre-calculated INBLK 60$: CALL FILALL ; Go open output file entry BCC 100$ ; If no errors, proceed. BIT #XIF$OP,OPTACT ; /DEVICE/FILE? BNE 30$ ; try some more CMPB FORMAT,#'Q ; New BUP disk? BNE 40$ ; old BUP file. Quit. BR 30$ ; try another file. ...... ; Do /DEVICE restore 80$: MOV TOTLLN,REMAIN CALL DEVFIT ; Open output volume NFS for /DEVICE ; Restore data until either the input volume is exhausted, or a file ; is complete. 100$: CLR OUTBLK ; Start at block zero 110$: TSTB INOPEN ; Is input already open? BNE 120$ MOVB #1,INOPEN ; Flag input as open CALL SIGSTA ; Signal start of restore CLR NOBBL ; Reset bad block counter ; Determine the number of blocks to transfer for the current input volume. 120$: MOV REMAIN,R0 ; How many blocks left to copy? CMP VOLREM,R0 ; Exceeds blks left on input volume? BHIS 130$ ; Branch if not. MOV VOLREM,R0 ; Otherwise, transfer what's available 130$: MOV R0,BLKCOP ; Do this many blocks on this volume ; Copy data from input volume to output 140$: MOV COPBLK,RBLOCK ; Get block chunk to read CALL XCOP ; Go restore of input volume TST REMAIN ; More blocks this file? BEQ 160$ ; If not, close output. ; Input volume is exhausted. Count it and check for done. 150$: .CLOSE INCHAN CLRB INOPEN INC VOLNUM ; Next volume number INC EXPCTD ; Next section number INCB VOLCOP ; Update volumes copied CMPB VOLCOP,NUMVOL ; Are all volumes copied? BNE 10$ ; Branch if not. ; Close output. 160$: BIT #XI$OPT,OPTACT ; /DEVICE restore? BEQ 170$ ; branch if not CALL UPDDIR ; If /DEVICE, fix final 170$: CMPB DEV1,#DEV.TT ; Unless outputing to TT, BEQ 175$ .CLOSE OUTCHA ; Make file permanent ; Any more files to do? 175$: BIT #,OPTACT ; /DEVICE? or file image? BNE 230$ ; If so, we are done. BIT #XIF$OP,OPTACT ; /DEVICE/FILE? BNE 180$ ; do some more CMPB FORMAT,#'Q ; New BUP disk? BNE 230$ ; If not, quit. ; Finished with individual file restore. Set it's date and protection ; attributes. Log the transfer on the console. 180$: CALL FINFIL ; Try for another file. 200$: TSTB INOPEN ; Input currently open? BNE 220$ ; If so, resume without opening. 210$: JMP 10$ ; If not, open it. ...... 220$: JMP 30$ ...... ; All done restoring. Print final DONE message and return to main. 230$: .CLOSE INCHAN CALLR RESDON ; Do REStore DONe stuff ...... ; and return to BUP main .DSABL LSB .SBTTL NEWINP - Mount a new input volume ;+ ; Mount a new input volume. If it is not the first, prompt the user. ; Check that the mounted volume is 1. a BUP volume, and 2. the next ; one in sequence (VALID). Get various pieces of information from ; the homeblock. ;- .ENABL LSB NEWINP: TSTB GIV.R ; /RT11 logical disk? BPL 10$ ; branch if not. MOVB #'Q,FORMAT ; fake a BUQ disk BR 50$ ; and skip next stuff. ...... 10$: TSTB GIV.Y ; /NOQUERY? BPL 20$ ; if not, always prompt. CMP EXPCTD,#1 ; First volume? BEQ 30$ ; If so, skip mount query. 20$: CALL MXPRMT ; Do RESTORE prompt/answer BNE 30$ ; Yes, proceed CALL QABOR ; No? Abort? BR 20$ ; Ok. try again. ...... 30$: CALL LOOKD ; Lookup input device (NFS) CALL VALID ; Is it the next valid BUP volume? 40$: ROR R1 ; Save C-bit in R1 .CLOSE INCHAN ; Close the NFS-mode lookup .if NE DEBUG .print #msg1 .endc ROL R1 ; Get the C-bit result of VALID BCS 20$ ; Not valid; ask for another volume. ; If this is the first volume, and it is a NEW BUP disk, look at the ; command line. If it is the form OUT:=IN:[xxx]/X then change it now to ; be equivalent to OUT:=IN:BACKUP.BUP/S[,xxx]/X/I/F. TSTB XFORM ; Is this done yet? BNE 80$ ; Branch if so. CMPB FORMAT,#'Q ; New format? BNE 80$ ; Branch if not. 50$: CALL XFORMS ; Transform command, get default name INCB XFORM ; Set flag indicating this is done. ; Open the BUP "file" using the volume's directory entry. 80$: CLR NPREFB ; Assume no prefix block CALL LOOKF ; Open the saveset area as a file BCS 20$ ; Prompt again if error. CLR INBLK ; Start with block zero CMPB FORMAT,#'Q ; New BUP format? BEQ 90$ ; branch if so. ; Get the actual size of the section of an OLD BUP disk CALL GSIZE ; Get size of input device BCS 20$ ; Branch if error MOV R1,SIZBLK ; Save device size CALL FILCAL ; Calculate section sizes MOV DISKVN,THISEC ; For old BUP disks, volumes=sections MOV BLKAVA,SEC1LN ; section 1 length MOV BLKAVA,INTMLN ; = intermediate length MOV SMALLE,SECNLN ; final section length BR 160$ ; done ...... ; Get information from new-style "BUQ" prefix block 90$: TSTB GIV.R ; /RT11 logical disk? BMI 190$ ; don't read prefix block if so. MOV #LBLBUF,R1 .READW #EMTARE,INCHAN,R1,#256.,#0 ; Read prefix block BCC 120$ .ERR #ERRARE,#RE1,LEVEL=ERROR,RETURN=YES,FILE=#INFIL BR 40$ ; offer another chance ...... 100$: .PRINT #NOTBUQ ; Not a BUQ saveset section 110$: .CLOSE INCHAN ; close JMP 20$ ; and ask again ...... 120$: MOV BQ.S1LN(R1),SEC1LN ; Get FIRST section length MOV BQ.SILN(R1),INTMLN ; Get INTERMEDIATE section length MOV BQ.SLLN(R1),SECNLN ; Get LAST section length MOV BQ.TTLN(R1),TOTLLN ; Get total saveset length MOV BQ.NOSC(R1),NUMVOL ; Get number of sections MOV BQ.SECN(R1),THISEC ; Get THIS section number MOV (R1)+,NPREFB ; Adjust for prefix block(s) MOV #3,R3 ; Check 3 words MOV #IDCHK,R2 ; against local model 130$: CMP (R1)+,(R2)+ ; "BUQ ", Version 5 BNE 100$ ; If not, error and re-prompt. DEC R3 BGT 130$ ; Check to make sure that the volume (section) is consistent CMP THISEC,EXPCTD ; If not, is it the expected section? BEQ 140$ ; Branch if so. .CLOSE INCHAN ; Otherwise, close this one, MOV #VOL,R1 ;<-E-Wrong volume number> CALL EERR MOV DISKVN,R1 ; Mounted disk's volume number MOV THISEC,R0 ; Mounted section's number SUB EXPCTD,R0 ; how many backwards from here? SUB R0,R1 ; this should be the correct vol no. MOV R1,VOLNUM JMP 20$ ...... ; Verify the backup date and time against that saved from the 1st section 140$: MOV DISKVN,VOLNUM ; Let volume_wanted = volume_got MOV #,R1 ; point to prefix block's date MOV #DATIME,R2 ; point to save(d,ing) date/time area MOV #5,R3 ; no. of words to move/compare CMP EXPCTD,#1 ; First section? BNE 170$ ; branch if not. 150$: MOV (R1)+,(R2)+ ; save time-of-backup DEC R3 BGT 150$ 160$: BR 200$ ...... 170$: CMP (R1)+,(R2)+ ; compare time values BNE 180$ DEC R3 BGT 170$ BR 200$ ; proceed if all are equal. ...... 180$: MOV #SET,R1 ;<-E-Volume does not belong> CALL EERR BR 110$ ; re-prompt ...... ; The request is to restore files from a native RT-11 logical disk file. ; Fake the code into thinking that a valid BUP volume is mounted with one ; saveset section and no prefix block. 190$: MOV #1,R2 ; (1 is a popular number) MOV R2,THISEC ; 1st and only section MOV R2,VOLNUM ; pretend it's volume 1 desired, MOV R2,DISKVN ; and volume 1 inserted. MOV BLKAVA,SEC1LN ; of this length. 200$: .DSABL LSB .ENABL LSB ; Determine the first and last block numbers associated with this section MOV #1,R2 ; starting with section 1, CLR R3 ; first block number MOV SEC1LN,R0 ; get its last block no. DEC R0 10$: CMP THISEC,R2 ; is this the section we're on? BEQ 30$ INC R2 ; try next section MOV R0,R3 ; previous last plus 1 = next first INC R3 ; new first block no. CMP R2,NUMVOL ; is it the last section? BEQ 20$ ; branch if so. ADD INTMLN,R0 ; otherwise, add intermediate length BR 10$ ...... 20$: ADD SECNLN,R0 ; last section - last block 30$: MOV R0,LASTBL ; save last block of section MOV R3,FRSTBL ; save first block of section 40$: SUB NPREFB,BLKAVA ; account for prefix block MOV BLKAVA,VOLREM ; remaining space in section ADD NPREFB,INBLK ; starting block number RETURN ...... .DSABL LSB .SBTTL VALID - Check for valid restore volume ;+ ; Three things are checked: ; ; 1- the volume is a backup volume, ; ; 2- the file requested exists in this volume, ; (only if a filename was given in the command line) ; ; 3- the volume number is the right one. ; ; These are checked in order; a failure in any of the conditions ; makes the subroutine print a message indicating the failure and ; returns to caller. ; ; If a restore file operation is requested and a filename is not included ; in the command line, then the filename is not checked for this volume. ; Instead, the filename for that volume is used to check the subsequent ; volumes. ; ; In addition, the number of volumes that make up the input set is obtained, ; as well as the size of the smallest volume. This will be used later to ; calculate the number of input blocks to be restored. ; ; CALL VALID ; ; C-BIT = 0 SUCCESS ; = 1 ERROR ;- .ENABL LSB VALID: MOV INCHAN,R1 ; On input channel, CALL CHKVID ; Is it a backup volume? BCC 30$ ; Yes, branch TSTB NOTBAK ; Not a backup volume? BPL 20$ ; Yes it is. Other problem. branch 10$: MOV #BAK,R1 ;<-E-Not a BACKUP volume> CALL EERR 20$: CLRB NOTBAK ; Clear flag BR 110$ ; set carry and exit. ...... ; Do volume number check. If this is the first section (EXPCTD=1) and ; the disk is a new BUP disk, relax the test. ; Determine whether the input volume is an OLD BUP DISK or a NEW BUP DISK. 30$: MOV DK.VOL(R2),DISKVN ; Get the volume number MOVB (R2),FORMAT ; Get the BU(P,Q) format code CMP EXPCTD,#1 ; Is this the first section? BNE 40$ ; If not, do test. CMPB FORMAT,#'Q ; is it the new style? BEQ 60$ ; if so, we can tolerate non-1 vol-num 40$: CMP DISKVN,VOLNUM ; Is it the right volume? BEQ 50$ ; Yes, branch; get total MOV #VOL,R1 CALL EERR ;<-E-Wrong volume number> BR 110$ ; Return with error. ...... 50$: CMPB FORMAT,#'Q ; does the tag say "BUQ"? BNE 70$ ; Branch if not. ; It's a NEW BUP disk. 60$: RETURN ...... ; It's an OLD BUP disk. ; Check that if an input filespec was given, it matches the ASCII ; name in the old bup disk's homeblock. 70$: ADD #,R2 ; Point to name area in homeblock CMP EXPCTD,#1 ; Beyond 1st volume? BHI 80$ ; If so, compare names TST NONAME ; Was an input filename supplied? BMI 120$ ; No, branch; no name check 80$: MOV #INNAM,R3 ; Point to user-specified input file MOV #9.,R4 ; Size of input name & ext 90$: CMPB (R3)+,(R2)+ ; Compare letters BNE 100$ ; Branch if not equal DEC R4 ; Decrement count BNE 90$ ; Repeat if not zero BR 160$ ; Finish good filename ...... 100$: MOV #INFIL,R3 ; R3 -> input file spec .ERR #ERRARE,#LO1,LEVEL=ERROR,RETURN=YES,FILE=R3 ;;<-F-File not found > 110$: BR 180$ ; Set carry before leaving ...... ; Get the saveset name from the old BUP disk homeblock. 120$: CMP EXPCTD,#1 ; Only for the 1st volume, BNE 170$ MOV #INNAM,R3 ; R3 -> input ASCII filename MOV #9.,R4 ; R4 = number of characters 130$: MOVB (R2)+,(R3)+ ; Store ASCII DEC R4 ; Until the whole spec BNE 130$ ; Has been stored ; Convert the name to RAD50 MOV #INFIL,R3 ; R3 -> RAD50 in device TST (R3)+ ; R3 -> filename MOV #INNAM,R1 ; R1 -> ASCII in filename CALL CNVRT3 ; Convert to 3 RAD50 words ; For FILE restores, if no output name was specified, move the input ; name to the output spec. 140$: BITB #X$OPT,OPTACT ; Restore FILE operation? BEQ 160$ ; No, skip next BIC #100000,NONAME ; Clear noname flg for next TSTB NONAME ; Output filename given? BPL 160$ ; Yes, branch MOV #INNAM,R3 ; R3 -> input ASCII name MOV #9.,R4 ; R4 = count MOV #OUTNAM,R1 ; R1 -> output ASCII name 150$: MOVB (R3)+,(R1)+ ; Store characters DEC R4 ; loop until done BNE 150$ MOV #OUTFIL,R3 ; R3 -> RAD50 out device TST (R3)+ ; R3 -> RAD50 out filename MOV #OUTNAM,R1 ; R1 -> ASCII out filename CALL CNVRT3 ; Convert to 3 RAD50 words 160$: MOV (R2)+,DISKVN ; Get disk's volume number MOV (R2)+,NUMVOL ; Get total number of volumes MOV (R2),SMALLE ; Get size of smallest volume 170$: TST (PC)+ ; Good return. 180$: SEC ; Error return. RETURN ...... .DSABL LSB .SBTTL PREPOU - Prepare for output ;+ ; One-time initialization of output stuff ;- .ENABL LSB ; Fetch output device handler PREPOU: MOV NHDRAD,R1 ; R1 = adr to load handler MOV OUTFIL,DEVSPC ; Get device spec of OUTPUT CALL FETHAN ; fetch output handler ; R0 points to next available memory. ; Allocate I/O buffers for restoring 10$: MOV R0,BUFADD ; save address for buffer space MOV R0,NHDRAD ; (shouldn't need this, but...) CALL XMBUFR ; If XM, point to high mem buffer. INCB OUTINI ; Flag OUTPUT initialization done .if NE DEBUG .print #msg3 .endc CALL CHKBBR ; Check for BBR device ; Calculate the number of blocks per I/O transfer. Set up two buffers ; for either non-verify double-buffering or a read/verify and write buffer. CALL DYNBUF ; Calculate memory available MOV R3,R2 ; R2 = blocks available for buffers ASR R2 ; TWO buffers needed - divide by two MOV R2,COPBLK ; save blocks to copy MOV #1,R0 TSTB BBRDEV BEQ 20$ INC R0 ; = 1 (Not BBRDEV) or 2 (BBRDEV) 20$: CMP R2,R0 ; Enough blocks in buffer? BHIS 30$ ; Hopefully so. MOV #MEM,R1 ; <-F-Insufficient memory> JMP FATAL ...... 30$: .if ne debug .print #msg33 ; display no. of blocks per buffer call decima .print #crlf .endc; ne debug SWAB R2 ; convert to wordcount ASL R2 ; R2 = bytecount MOV BUFADD,VERBUF ; init verify buffer pointer ADD R2,VERBUF RETURN ...... .DSABL LSB .SBTTL NEXFIL - Set up for an input file ;+ ; If this is a FILE restore from a /DEVICE container (saveset), ; and it has not been done yet, make a copy of the directory blocks ; in a temporary file. ; ; If this is a FILE restore from an OLD BUP disk, simply return. ; ; Return code: If a file is available, the carry bit is clear. ; otherwise, it is set. ;- .ENABL LSB NEXFIL: JSR R5,$SAVR1 ; Save registers CMPB FORMAT,#'P ; Old BUP disk? BNE 10$ BIT #XIF$OP,OPTACT ;*C* RESTORE/DEV/FILE ? BNE 10$ ;*C* MOV TOTLLN,REMAIN ;*C* Old BUP file or device size MOV TOTLLN,FILSIZ ;*C* RETURN ; Return - simple old BUP disk ...... 10$: TSTB TMPFLG ; Temp file stored yet? BNE 60$ ; Branch if so. ; This code assumes that the directory portion of a saveset is NEVER ; split across volumes. The disk backup module makes sure of this. MOV INBLK,R1 ; first block of section ADD #6,R1 ; offset to directory blocks MOV BUFADD,R4 20$: .READW #EMTARE,INCHAN,R4,#512.,R1 BCS 80$ TSTB TMPFLG ; Temp file opened yet? BNE 50$ ; Branch if so MOV @R4,R3 ; get no. of segments ; Check to make sure that it's really an RT-11 directory BEQ 100$ ; If zero, that's screwy. CMP R3,#31. ; check against legal limit BHI 100$ ; Branch if invalid. ; Ok. Create a temporary file that will hold a copy of the saveset directory. MOV #6,R2 ; output block number MOV R3,TEMP2 ADD R3,TEMP2 ; number of blocks needed ADD R2,TEMP2 ; including boot blocks .SERR .ENTER #EMTARE,TMPCHA,#TMPFIL,TEMP2 BCC 40$ 30$: MOV #WR2,R1 ; Output error WF:BUPTMP.TMP MOV #TMPFIL,R3 JMP FATAL3 ...... 40$: .HERR 50$: INCB TMPFLG ; Set flag, count segments .WRITW #EMTARE,TMPCHA,R4,#512.,R2 BCS 30$ ADD #2,R1 ; bump input block number by 2 ADD #2,R2 ; bump output block number by 2 DEC R3 ; Count down segments BGT 20$ ; loop if more to get. ; Call IGTDIR to specify the temporary file as an input directory MOV #TMPCHA,IGDCHN ; Setup for input on temp channel MOV #IGTDIR,R0 MOV #IGDBLK,R5 ; point to arg block CALL CALL$F ; call IGTDIR TST R0 ; look at return code BLT 30$ ; branch on error ; Get next entry 60$: MOV #IGTENT,R0 MOV #IGEBLK,R5 ; point to arg block CALL CALL$F ; call IGTENT TSTB GIV.P ; /EXCLUDE in effect? BPL 65$ ; branch if not CMP R0,#E.NOMA ; not a match? BEQ 110$ ; then use it. TST R0 BPL 60$ ; /EXCLUDE the matching entry 65$: TST R0 ; look at return code BPL 110$ ; Branch if good. SEC ; Indicate no more or error 70$: RETURN ...... 80$: MOV #RE1,R1 ; I/O error reading input 90$: MOV #INFIL,R3 JMP FATAL3 ...... 100$: MOV #IND,R1 ; Saveset not RT-11 format BR 90$ ...... ; A directory entry has been found in the temporary container. Get ; its vitals. 110$: MOV #DIRENT,R1 ; Point to directory entry BIS R0,FNDBTS ; keep record of files found (bitmap) BIT #400,R0 ; Did IGTENT indicate EXACT_EXTENSION? BNE 120$ ; branch if so. CMP E.TYPE(R1),#^RSYS ; Is it a .SYS file? BNE 120$ ; branch if not. TSTB SYSOUT ; Restoring to SY:? BEQ 120$ ; branch if not. MOVB GIV.E,R0 ; /SYSTEM (/E) given? BISB VONLY,R0 ; or /VERIFY:ONLY? BMI 120$ ; branch (let it go) if so. TSTB NSYMSG ; -No .SYS action. Message out yet? BNE 60$ ; If so, go ask for next file entry. .ERR #ERRARE,#NSY,LEVEL=WARNING,RETURN=YES INCB NSYMSG ; Turn off similar future messages BR 60$ ; Get next file entry. ...... 120$: MOV E.LENG(R1),REMAIN ; File length -> blocks to copy MOV REMAIN,FILSIZ ; save size for file creation MOV FILBLK,INBLK ; Get absolute starting block ; Determine whether it's time to change input volumes. MOV LASTBL,R2 ; get last absolute block on this vol 130$: CMP INBLK,R2 ; Beyond this volume? BLOS 140$ ; Branch if not. INC VOLNUM ; Next volume expected INC EXPCTD ; Next section expected ADD INTMLN,R2 ; get last block of NEXT volume BCC 130$ ; Loop unless ADD overflowed. ; Convert the absolute starting block number in INBLK to a value relative ; to the section of the volume that will be mounted. 140$: MOV #1,R3 ; Starting from section 1, MOV SEC1LN,R4 ; 1st time, reduce by 1st sec len 150$: CMP R3,EXPCTD ; for each section, BEQ 160$ ; reduce INBLK by the previous INC R3 ; section length. SUB R4,INBLK ; Adjust INBLK for new volume. MOV INTMLN,R4 ; after 1st, sub intermediate len BR 150$ ; loop until this section reached ...... 160$: TSTB NONAME ; was an output name specified? BPL 170$ ; branch if so. MOV R1,R3 ; Point to entry ADD #E.NAME,R3 ; Point to name MOV #,R4 ; Point to output filename area .REPT 3 MOV (R3)+,(R4)+ ; Move entry name to output area .ENDR 170$: MOV BLKAVA,VOLREM ; Calculate remaining in section SUB INBLK,VOLREM ; blocks to end of section 180$: ADD NPREFB,INBLK ; Account for prefix block CLC ; indicate success RETURN ...... .DSABL LSB .SBTTL FILCAL - Calculate restore file size ;+ ; For OLD BUP disks... ; Using the number of volumes, the size of the smallest volume and ; the size of the input device is the number of blocks to be restored ; calculated. ;- .ENABL LSB FILCAL: MOV SIZBLK,R1 ; Get volume size, SUB #RESBLK,R1 ; Get actual blocks to copy... MOV R1,BLKAVA ; ... and store it CLR R2 ; Store calculation size here MOV NUMVOL,R3 ; Get number of volumes 10$: ADD R1,R2 ; Add a volume size DEC R3 ; Count it CMP #1,R3 ; Is this the last volume? BNE 10$ ; No, branch; repeat ADD SMALLE,R2 ; Add smallest volume MOV R2,TOTLLN ; Store total blocks RETURN ; Return ...... .DSABL LSB .SBTTL XCOP - Copy operation for one input volume ;+ ; Copy blocks from the backup saveset to the destination device or file. ; BLKCOP is the number of blocks yet to copy for this volume. RBLOCK is ; the number of blocks to transfer per I/O. ; ; If /NOVERIFY, then double-buffering is employed. If /VERIFY, then ; one half of the buffer is used for RESTORE and the other half for ; a verify buffer. ; ; R0 is a scratch register ; R1 ; R2 is the current READ/VERIFY buffer address (it bounces back and forth) ; R3 is the current WRITE buffer address (so does this) ; R4 holds the I/O transfer word count ; R5 ;- .ENABL LSB XCOP: MOV BUFADD,R2 ; 1st half of buffer MOV VERBUF,R3 ; 2nd half of buffer CALL WAITO ; wait for any writes to complete ; to ensure both buffers free. 10$: MOV BLKCOP,R0 ; Get "blocks yet to copy" BEQ 70$ ; Branch if no more blocks MOV RBLOCK,R4 ; Get blocking factor CMP R4,R0 ; compare with blocks yet to copy BLOS 20$ ; Go do full blocking factor. MOV R0,R4 ; only R0 more blocks to do... MOV R0,RBLOCK ; save for updates below. 20$: SWAB R4 ; make block count a word count CALL GRBLKS ; Get blocks from input via R2 MOV R2,R0 ; Swap the buffer pointers MOV R3,R2 MOV R0,R3 CALL WAITO ; wait for previous write to complete BIT #XI$OPT,OPTACT ; /DEVICE operation? BEQ 30$ ; Branch if not. TST OUTBLK ; Writing first output block? BNE 30$ ; Branch if not. CALL CHKHMB ; Check for home blk on DM or DL 30$: CMPB VONLY,#WRDISK ; Doing /VERIFY:ONLY? BNE 50$ ; If so, branch around write. .WRITE #EMTARE,OUTCHA,R3,R4,OUTBLK ; Write to output block BCC 40$ ; Branch if no error JMP FATALO ; .WRITE rejected ...... ; If VERIFYing, wait for write to complete, then use the VERIFY ; routine to read the blocks back from the destination disk. 40$: TSTB GIV.V ; /VERIFY? BPL 60$ ; No, branch. 50$: CALL WAITO ; wait for write to complete CALL VERIFY ; go verify the write. ; Update counters and pointers for the next transfer. 60$: MOV RBLOCK,R0 ; Let R0 hold the block factor ADD R0,INBLK ; Update input block number ADD R0,OUTBLK ; Update output log block no. SUB R0,REMAIN ; Update 'total blocks to restore' SUB R0,BLKCOP ; Update 'blocks to copy, this volume' BNE 10$ ; If more on this volume, continue. 70$: RETURN ...... .SBTTL WAITO - Wait for write completion ; Wait for output to complete and report any errors WAITO: .WAIT OUTCHA ; wait for write to complete BCC 80$ CALL ERRRO ; Report output error 80$: RETURN ...... .DSABL LSB .SBTTL GRBLKS - Read blocks from input ;+ ; On entry, ; INBLK = starting block number ; R2 = buffer address (preserved) ; R4 = wordcount ; ; On exit, ; R2 = buffer address (preserved) ; R3 = pointer to other buffer (preserved) ; R4 = total wordcount ;- .ENABL LSB GRBLKS: .READW #EMTARE,INCHAN,R2,R4,INBLK BCC 60$ ; Error detected on bulk read. Try reading block by block. MOV R4,-(SP) ; save wcnt for return. MOV R2,-(SP) ; and buffer address MOV INBLK,R1 ; input block number 20$: .READW #EMTARE,INCHAN,R2,#256.,R1 BCC 40$ .PRINT #JNKTXT ; print message MOV R1,-(SP) MOV R2,-(SP) MOV R4,-(SP) MOV R1,R2 ; get input block no. CALL DECIMA ; print block no., .PRINT #OF ; print ' of ' MOV #INFIL,R0 ; point to input name MOV #LBLBUF,R1 ; point to a free area CALL $FNASC ; convert device name to ASCII CLRB @R1 ; null terminate it .PRINT #LBLBUF ; print saveset name and end of line MOV (SP)+,R4 MOV (SP)+,R2 ; restore registers MOV (SP)+,R1 40$: ADD #512.,R2 ; move buffer address pointer INC R1 ; point to next block SUB #256.,R4 ; subtract from total wordcount BNE 20$ ; if more to read, read some more. MOV (SP)+,R2 ; Restore buffer address, MOV (SP)+,R4 ; Return with wordcount in R4. 60$: RETURN ...... .DSABL LSB .SBTTL VERIFY - Verify copy operation ;+ ; Verify the last write operation by reading what was written ; and comparing it to the original data. ; ; On entry, ; R2 = verify buffer address ; R3 = write buffer address ; R4 = word count ; OUTBLK = starting output block number ; ; On exit ; All registers preserved ;- .ENABL LSB VERIFY: JSR R5,$SAVR1 ; Save ALL registers MOV #-1,TEMP2 MOV #1,R5 ; Assume 1 read required MOV OUTBLK,R1 10$: .READW #EMTARE,OUTCHA,R2,R4,R1 ; read blocks that were just written BCC 50$ ; branch if no error CMP R4,#256. ; Error. Already in single-block mode? BEQ 40$ ; branch if so. ; ; An error occurred while reading back the set of blocks. Reduce wordcount ; to 256, and determine number of single-block reads. ; MOV R4,R5 ; get the word count, SWAB R5 ; get number of single block reads ; Loop, reading each individual block 30$: MOV #256.,R4 ; make a new wordcount for 1 block BR 10$ ; Read in single-block mode. ...... 40$: MOV R1,-(SP) ; save block number across error... CALL ERRRI ; -E-Input error on OUTFIL MOV (SP)+,R1 ; Verify the contents of the main buffer with the verify buffer. 50$: CLR OFFSET 60$: CMP (R2)+,(R3)+ ; Compare words... BEQ 70$ ; Branch if OK ; Data mismatch. Report "Verification Error at block nnnnn" MOV OFFSET,R0 ; current bulk word count BIC #377,R0 ; clear out inter-block junk SWAB R0 ; change word count to blk count ADD R1,R0 ; Add base block CMP R0,TEMP2 ; same block again? BEQ 70$ MOV R0,TEMP2 ; save the block number MOV R2,-(SP) ; save pointer MOV R4,-(SP) ; save base block MOV R0,R2 ; R2 is the print register CALL RVERER ; Report verify error MOV (SP)+,R4 ; restore base block MOV (SP)+,R2 ; restore pointer CMP VBAD,..MBAD ; Too many errors yet? BLT 70$ MOV #MIS,R1 ; -F-Too many verify errors JMP FATAL ...... 70$: INC OFFSET SOB R4,60$ ; next word INC R1 ; next block SOB R5,30$ RETURN ; (restores all registers) ...... .DSABL LSB .SBTTL LOOKD - Do NFS lookup on INPUT device .ENABL LSB LOOKD: .SERR MOV INPDEV,DEVSPC .LOOKUP #EMTARE,INCHAN,#DEVSPC ; Open channel to input device BCC 20$ ; Branch if no error ; TSTB @#$ERRBY ; Which class of error? ; BMI 10$ ; .SERR in effect. ; MOV #LO2,R1 ; -F-Channel in use ; JMP FATAL ; ...... 10$: .PURGE INCHAN MOV #WR1,R1 ; -F-Directory I/O error JMP FATALD ...... 20$: .HERR RETURN ...... .DSABL LSB .SBTTL LOOKF - LOOKUP the input saveset section by name ;+ ; Lookup the input saveset section by filename. (BUP disks have ; always had a normal RT-11 directory file entry) ;- .ENABL LSB LOOKF: .PVAL #EMTARE,#$BLKEY,#0 ; Force re-read of dir blocks ; by setting $BLKEY to zero .SERR .LOOKUP #EMTARE,INCHAN,#INFIL BCS 90$ MOV R0,BLKAVA ; Save section size .HERR RETURN ...... 90$: MOV #LO1,R1 ; ?-F-File not found xxxxxx MOV #INFIL,R3 JMP FATAL3 ; (in BUPSUB) ...... .DSABL LSB .SBTTL EERR - E-level errors with return ; R1 = error identifier EERR: .ERR #ERRARE,R1,LEVEL=ERROR,RETURN=YES RETURN ...... ; ?BUP-E-Output error ERRRO: MOV #WR2,R1 BR ERRRXO ...... ; ?BUP-E-Input error ERRRI: MOV #RE2,R1 ERRRXO: .ERR #ERRARE,R1,LEVEL=ERROR,RETURN=YES,FILE=#OUTFIL RETURN ...... .SBTTL Patch space ;+ ; Patch space for module BUPDRS ;- .PSECT PATCH,RW,I PATDRS::.BLKW 16. .END