.title recover2 recover files from damaged ODS-2 disk .ident /v01/ ;+ ; Recover2 recovers files from a damaged ODS-2 disk, using only the ; logical block number of the file's header in what is left of the ; index file. Another program, scan2, scans the whole disk looking ; for blocks that could be file headers and writes LBNs, FIDs, file ; names, and a few attributes in a sequential file called INDEXF.SEQ. ; ; That file is converted to an indexed file called INDEXF.IDX and ; massaged by Datatrieve to become input to this program. ; ; This program uses that file to get selected file headers from the ; damaged disk by LBN and rebuild the files on a good, mounted disk. ; ; Anthony E. Scandora, Jr. ; Science Applications International Corp. ; October 6, 1984 ;- .psect data,noexe,long ; ; Parameters unique to the disk being recovered ; These should be determined at run time, but I'm in a hurry ; dsksiz = 891072 ; size of an RA81 innam: .ascid /DUK1:/ ; name of volume to be scanned .align long ; ; Register usage: ; ; r10 number of blocks left to process in current buffer ; r9 pointer to current header block ; r8 id area in current header ; r7 map area in current header ; r6 access control area in current header ; r0-5 scratched by movc3 and used for temps ; ; r11 working VBN ; r10 working block count ; r7 current retrieval pointer ; r6 VBN of current retrieval pointer ; r5 LBN of current retrieval pointer ; r4 size of current retreival pointer ; r3 number of words of retrieval pointers left ; ; Definitions that do not have to be changed for every disk ; .library /fhdo2/ ; define file header offsets .mcall fhdo2$ fhdo2$ $atrdef ; define FIB attributes idxfab: $fab fac=get,fnm= idxrab: $rab fab=idxfab,ubf=idxrec,usz=idxsiz ;outfab: $fab dnm=,fac=,fna=hdfnam,fns=20,- ; xab=outall ;outall: $xaball nxt=outdat ;outdat: $xabdat nxt=outpro ;outpro: $xabpro nxt=outrdt ;outrdt: $xabrdt ;outrab: $rab fab=outfab outfab: $fab dnm=,fac=,fna=hdfnam,fns=20,fop=ufo outfib: .word 0,0 .address 10$ 10$: .blkb 0 outatt: .word atr$s_recattr,atr$c_recattr .address header+hdr2$_ufat .word atr$s_credate,atr$c_credate outcrd: .address 0 .word atr$s_revdate,atr$c_revdate outrvd: .address 0 .word atr$s_expdate,atr$c_expdate outexd: .address 0 .word atr$s_bakdate,atr$c_bakdate outbkd: .address 0 .word atr$s_uic,atr$c_uic .address header+hdr2$l_fown .word atr$s_fpro,atr$c_fpro .address header+hdr2$w_fpro .long 0 iniosb: .blkw 4 ; I/O status block for input channel incnt: .blkl ; count of bytes read assin: $assign devnam=innam,chan=rdqio+qiow$_chan rdefn = 1 ; efn for reading input disk rdqio: $qiow rdefn,,io$_readlblk,iniosb,,,blocks rdhdr: $qiow rdefn,,io$_readlblk,iniosb,,,header,512 ouiosb: .blkw 4 ; I/O status block for output channel oucnt: .blkl ; count of bytes written outsiz: .blkl ; allocated size of file wrefn = 2 ; efn for writing output file wrqio: $qiow wrefn,,io$_writevblk,ouiosb,,,blocks deacou: $qiow wrefn,,io$_deaccess,ouiosb,,,outfib,,,,outatt msgdsc: .blkl ; actual size of message buffer .address msgbuf msgds0: .long 132 ; msgbuf size for $fao input .address msgbuf msgbuf: .blkb 132 ; output message buffer ; ; record from indexf.seq ; idxrec: hdlbn: .blkl ; LBN of current header hdfnum: .blkl ; file number of current header hdfseq: .blkw ; sequence number of current header hdfseg: .blkw ; file segment number hdefnu: .blkl ; file number of extension header hdefsq: .blkw ; sequence number of extension header status: .blkb ; blank or '-' if deleted direct: .blkb ; blank or 'D' if directory file hdbfnu: .blkl ; file number of back pointer hdbfsq: .blkw ; file sequence number of back pointer hdfnam: .blkb 20 ; file name hdcrdt: .blkw 4 ; file creation date hdefbk: .blkl ; logical end of file block hdhibk: .blkl ; high allocated block number idxsiz = .-idxrec ; indexf.idx record size namdsc: .long 20 ; filename descriptor .address hdfnam ; for filmsg filmsg: .ascid /!AS !UL/ cnterr: .ascid /Read !UL bytes, wrote !UL bytes for !UL block file/ .psect buffer,noexe,page header: .blkb 512 ; file header block bufsiz = 1000 ; do bufsiz blocks at a time blocks: .blkb bufsiz*512 ; buffer for those blocks .page .sbttl the main program .psect recover2,nowrt,nord .entry recover2,^m callg assin,g^sys$assign ; assign channel to input disk blbs r0,20$ pushl r0 ; complain if no good calls #1,g^lib$stop 20$: movw rdqio+qiow$_chan,rdhdr+qiow$_chan $open idxfab ; open index file blbs r0,30$ pushl r0 calls #1,g^lib$stop 30$: $connect idxrab ; connect rab to index file blbs r0,100$ pushl r0 calls #1,g^lib$stop 100$: $get idxrab ; get another record blbs r0,110$ ret ; exit when no more records 110$: cmpb direct,#^a/D/ ; ignore directory files beqlu 100$ cmpb status,#^a/!/ ; just do files with ! in status field bnequ 100$ movl hdlbn,rdhdr+qiow$_p3 ; read header block callg rdhdr,g^sys$qiow blbc r0,118$ ; check system status movzwl iniosb,r0 ; check I/O status blbs r0,120$ 118$: pushl r0 ; complain about read error calls #1,g^lib$stop ; ; got a header, see if it's to be recovered and read its map ; 120$: movab header,r9 ; get header pointer tstw hdr2$w_fnum(r9) ; is header deleted? beqlu 110$ ; if so, ignore it movzbl (r9),r0 ; get id area pointer movaw (r9)[r0],r8 jsb mapini ; set up map area 130$: tstl r3 ; if map words all gone, done bleq 140$ jsb getmap ; get next pointer brb 130$ ; and get next pointer 140$: addl r4,r6 ; count last pointer decl r6 ; VBN is off the end of the file movl r6,outsiz ; save because $create writes alq pushl r6 ; write file size pushaq namdsc ; and name pushaq msgds0 ; push full size messsage buffer pushaw msgdsc ; push pointer to actual size pushaq filmsg ; push format string calls #5,g^sys$fao ; format the error message blbc r0,141$ ; complain if FAO failed pushaq msgdsc ; print file message calls #1,g^lib$put_output blbs r0,142$ ; if put failed, die with its error movl #4,r0 ; otherwise, die with fatal error 141$: pushl r0 ; push error code calls #1,g^lib$stop ; and die 142$: .page .sbttl create the file .if defined never ; this turned out to be a mess ; I was going to copy all the stuff from the input header to XABs ; and let RMS do a block copy. It's too scattered, and there are ; a lot of funny cases, like there is a special xab if you really ; want to do a revision date. I changed it to UFO access and did ; my own QIOs. movl r6,outall+xab$l_alq ; put actual length of file in xab clrb outall+xab$b_aop ; start with no allocation options bitb #hdr2$m_ucha_con,hdr2$b_ucha(r9) ; make it contiguous? beqlu 143$ bisb #xab$m_ctg,outall+xab$b_aop 143$: bitb #hdr2$m_ucha_cnb,hdr2$b_ucha(r9) ; or contig. best try? beqlu 146$ bisb #xab$m_cbt,outall+xab$b_aop 146$: movb hdr2$_ufat+00(r9),outfab+fab$b_org ; file organization movb hdr2$_ufat+01(r9),outfab+fab$b_rat ; record attributes movb hdr2$_ufat+14(r9),outall+xab$b_bkz ; bucket size movb hdr2$_ufat+15(r9),outfab+fab$b_fsz ; fixed control area size movw hdr2$_ufat+16(r9),outfab+fab$w_mrs ; maximum record size movw hdr2$_ufat+18(r9),outall+xab$w_deq ; default extend quantity movq id2$w_rvno(r8),outdat+xab$w_rvn ; revision number movq id2$q_crdt(r8),outdat+xab$q_cdt ; creation date ; movq id2$q_rvdt(r8),outdat+xab$q_rdt ; revision date clrq outdat+xab$q_rdt ; revision date movq id2$q_exdt(r8),outdat+xab$q_edt ; expiration date ; movq id2$q_bkdt(r8),outdat+xab$q_bdt ; last backup date clrq outdat+xab$q_bdt ; last backup date movl hdr2$_ufat+999(r9),outpro+xab$l_uic ; owner UIC movw hdr2$_ufat+999(r9),outpro+xab$w_pro ; file protection .iff ;df never ; use ACP attribute block ; This is easier. Just copy a few essentials like the size of the ; file and contiguous bits to the FAB and open for UFO access. movl r6,outfab+fab$l_alq ; size of file from retrieval pointers movl #fab$m_ufo,outfab+fab$l_fop ; start with no allocation options bitb #hdr2$m_ucha_con,hdr2$b_ucha(r9) ; make it contiguous? beqlu 143$ bisl #fab$m_ctg,outfab+fab$l_fop 143$: bitb #hdr2$m_ucha_cnb,hdr2$b_ucha(r9) ; or contig. best try? beqlu 146$ bisl #fab$m_cbt,outfab+fab$l_fop 146$: movaq id2$q_crdt(r8),outcrd ; put addresses in attribute block movaq id2$q_rvdt(r8),outrvd movaq id2$q_exdt(r8),outexd movaq id2$q_bkdt(r8),outbkd .endc clrl incnt ; clear the byte counters clrl oucnt $create outfab ; create file blbs r0,200$ pushl r0 ; complain if file create error calls #1,g^lib$stop 200$: movw outfab+fab$l_stv,wrqio+qiow$_chan ; write channel to wrqio movw outfab+fab$l_stv,deacou+qiow$_chan ; write channel to deacou .page .sbttl copy the blocks and deaccess the file jsb mapini ; set up pointers for getmap 210$: tstl r3 ; any retrieval pointers left? bgtr 215$ jmp 300$ 215$: jsb getmap ; get next retrieval pointer movab blocks,rdqio+qiow$_p1 ; buffer 220$: movl r4,r10 ; number of blocks for this pointer cmpl r10,#bufsiz ; will it fit? blequ 222$ movl #bufsiz,r10 ; enforce maximum buffer length 222$: ashl #9,r10,rdqio+qiow$_p2 ; byte count movl r5,rdqio+qiow$_p3 ; LBN callg rdqio,g^sys$qiow ; read the blocks blbc r0,225$ ; check system status movzwl iniosb,r0 ; check I/O status blbs r0,230$ 225$: pushl r0 ; complain about read error calls #1,g^lib$stop 230$: addl iniosb+2,incnt ; count the bytes read movl rdqio+qiow$_p2,wrqio+qiow$_p2 ; number of bytes to write movl r6,wrqio+qiow$_p3 ; VBN to write to callg wrqio,g^sys$qiow ; write the blocks blbc r0,237$ ; check system status movzwl ouiosb,r0 ; check I/O status blbs r0,240$ ; do next group of blocks 237$: pushl r0 ; complain about write error calls #1,g^lib$stop 240$: addl ouiosb+2,oucnt ; count the bytes written addl r10,r5 ; bump LBN addl r10,r6 ; bump VBN subl r10,r4 ; count the blocks bgtr 250$ ; get the rest of a big retrieval pointer brw 210$ ; get the next retreival pointer 250$: brw 220$ 300$: ashl #9,outsiz,r0 ; compute length of file in bytes cmpl incnt,r0 ; check number of bytes read bnequ 305$ cmpl oucnt,r0 ; check number of bytes written beqlu 306$ 305$: pushl outsiz ; push file length pushl oucnt ; push out count pushl incnt ; push in count pushaq msgds0 ; push full size messsage buffer pushaw msgdsc ; push pointer to actual size pushaq cnterr ; push format string calls #6,g^sys$fao ; format the error message blbc r0,3055$ ; if FAO failed, die with its error pushaq msgdsc ; print byte count error message calls #1,g^lib$put_output blbc r0,3055$ ; if put failed, die with its error movl #4,r0 ; otherwise, die with fatal error 3055$: pushl r0 ; push error code calls #1,g^lib$stop ; and die 306$: callg deacou,g^sys$qiow ; deaccess file blbc r0,307$ ; check system status movzwl ouiosb,r0 ; check I/O status blbs r0,310$ 307$: pushl r0 ; complain if deaccess error calls #1,g^lib$stop 310$: $dassgn_s wrqio+qiow$_chan ; deassign channel blbs r0,320$ pushl r0 ; complain if deassign error calls #1,g^lib$stop 320$: brw 100$ ; do next one .page .sbttl retrieval pointer routines .psect mapini,nord,nowrt mapini: movzbl 1(r9),r1 ; get map area pointer movaw (r9)[r1],r7 movzbl hdr2$b_use(r9),r3 ; number of words of pointers in use movl #1,r6 ; first VBN in file clrl r4 ; starting with no blocks rsb .psect getmap,nord,nowrt getmap: addl r4,r6 ; bump VBN of current file tstl r3 ; any map words left? blequ 505$ movzwl (r7),r0 ; get first word of retrieval pointer ashl #-14,r0,r0 ; get type bits caseb r0,#0,#3 410$: .word 500$-410$,510$-410$,520$-410$,530$-410$ 500$: tstw (r7)+ ; placement control, just eat the word decl r3 ; and count it 505$: clrl r4 ; return nothing rsb 510$: movzbl (r7)+,r4 ; format 1 - four bytes, get count incl r4 movzbl (r7)+,r5 ; get high bits of LBN bicb #64,r5 ; turn off format bit ashl #16,r5,r5 ; put high bits in high bits of r5 bisw (r7)+,r5 ; or in low word of LBN subl2 #2,r3 ; count two words rsb 520$: movzwl (r7)+,r4 ; format 2 - six bytes, get count bicw #^x8000,r4 ; turn off format bit incl r4 movl (r7)+,r5 ; get LBN subl2 #3,r3 ; count three words rsb 530$: movzwl (r7)+,r4 ; format 3 - eight bytes, get high count bicw #^xC000,r4 ; turn off format bits ashl #16,r4,r4 ; put high bits up high bisw (r7)+,r4 ; or in low bits of LBN incl r4 movl (r7)+,r5 ; get LBN subl2 #4,r3 ; count four wordsOM rsb .end recover2