	.title	scan2	scan ODS-2 for file headers
	.ident	/v01/

;+
; Scan2 scans all of the blocks in an ODS-2 disk volume for potential
; file headers and writes candidates out to a sequential file.  This
; is the first step in recovering a disk that should not have been
; initialized.
;
; Anthony E. Scandora, Jr.
; Science Applications International Corp.
; October 5, 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	current block in buffer
; r8	id area in current block
; r7	map area in current block
; r6	access control area in current block
; r0-5	scratched by movc3 and used for temps
;
; Definitions that do not have to be changed for every disk
;
	.library /fhdo2/		; define file header offsets
	.mcall	fhdo2$
	fhdo2$

iniosb:	.blkw	4			; I/O status block for input channel

outfab:	$fab	fac=put,fnm=<INDEXF.SEQ>,org=seq,rat=cr,rfm=var
outrab:	$rab	fab=outfab,rbf=outrec,rsz=outsiz

assin:	$assign	devnam=innam,chan=rdqio+qiow$_chan
rdefn	=	1			; efn for reading input disk
rdqio:	$qiow	rdefn,,io$_readlblk,iniosb,,,blocks

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 for indexf.seq
;
outrec:
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
delete:	.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

outsiz	=	.-outrec		; indexf.seq record size

rdmsg:	.ascid	/Read !UL bytes (!UL blocks) from block !UL/

	.psect	buffer,noexe,page
bufsiz	=	1000			; do bufsiz blocks at a time
blocks:	.blkb	bufsiz*512		; buffer for those blocks
	.page
	.sbttl	the main program

	.psect	scan2,nowrt,nord

	.entry	scan2,^m<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>

	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$:	$create	outfab			; create output file
	blbs	r0,30$
	pushl	r0			; complain if can't create output file
	calls	#1,g^lib$stop
30$:	$connect outrab			; connect rab to output file
	blbs	r0,40$
	pushl	r0			; complain if can't connect
;40$:	movl	#445746,rdqio+qiow$_p3	; start at block zero
40$:	clrl	rdqio+qiow$_p3		; start at block zero
100$:	movl	rdqio+qiow$_p3,r0	; get number of next block to read
	addl3	#bufsiz,r0,r10		; compute last block to read + 1
	cmpl	r10,#dsksiz		; off the end of the disk?
	blss	110$
	movl	#dsksiz,r10		; if so, stop at the end
110$:	subl	r0,r10			; compute number of blocks to read
	bgtr	115$
	brw	900$			; done if none left
115$:	mull3	#512,r10,rdqio+qiow$_p2	; compute transfer size
	callg	rdqio,g^sys$qiow	; read the blocks
	blbc	r0,117$			; complain if system error
	movzwl	iniosb,r0		; check I/O status
	blbs	r0,120$			; complain if read error
117$:	pushl	r0
	calls	#1,g^lib$stop
120$:	pushl	rdqio+qiow$_p3		; push first block just read
	pushl	r10			; push number of blocks just read
	pushl	iniosb+2		; push actual number of bytes read
	pushaq	msgds0			; push full size message buffer
	pushaw	msgdsc			; push pointer to actual size
	pushaq	rdmsg			; push format string
	calls	#6,g^sys$fao		; format what was just read
	blbs	r0,130$
	pushl	r0			; complain if $fao failed
	calls	#1,g^lib$stop
130$:	pushaq	msgdsc			; push actual message descriptor
	calls	#1,g^lib$put_output	; tell operator what was just read
	movab	blocks,r9		; pointer to current block
200$:	decl	r10			; any blocks left in this buffer?
	bgeq	210$
	brw	100$			; get next buffer if done
210$:	jsb	check			; check the block
	addl	#512,r9			; point to next block in buffer
	incl	rdqio+qiow$_p3		; bump current block number
	brw	200$			; and get next block
900$:	$close	outfab			; done, close output file
	movl	#1,r0			; exit
	ret
	.page
	.sbttl	check a block to see if it is a header

	.psect	check,nowrt,nord	; so debug will be same as listing

check:	cmpw	6(r9),#^x0201		; is it structure 2 level 1?
	beqlu	310$			; if so, take it
	rsb				; if not, forget it
310$:	movzbl	(r9),r8			; word offset to ID area
	cmpb	r8,#hdr2$l_fown/2	; is header area big enough?
	blssu	390$			; if not, forget it
	movzbl	1(r9),r7		; word offset to map area
	subl3	r8,r7,r0		; word length of ID area
	cmpw	r0,#id2$q_exdt/2	; is ID area big enough?
	blssu	390$			; if not, forget it
	movzbl	2(r9),r6		; word offset to access control area
	subl3	r7,r6,r0		; word length of map area
	movzbl	hdr2$b_use(r9),r1	; actual number of map words in use
	cmpb	r0,r1			; is map area big enough?
	blssu	390$			; if not, forget it
	bisw3	hdr2$w_fnum(r9),hdr2$w_frvn(r9),r0 ; file number & relative volume zero?
	bisw2	510(r9),r0		; and checksum zero?
	beqlu	350$			; if so, it's a deleted header
;
; got a possible file header
;
	movaw	(r9),r6			; compute checksum of header
	movzbl	#255,r1			; add up first 255 words
	clrl	r0
320$:	addw2	(r6)+,r0		; add in next word
	sobgtr	r1,320$
	cmpw	(r6),r0			; is checksum right?
	bneq	390$			; if not, forget it
	movw	hdr2$w_fnum(r9),hdfnum	; good header, get file number low word
	movzbw	hdr2$w_frvn+1(r9),hdfnum+2 ; get file number high byte
	movb	#^a/ /,delete		; mark the file not deleted
	brb	400$			; continue processing the header
;
; got a possible deleted file header
;
350$:	bitb	#hdr2$m_scha_mdl,hdr2$b_scha(r9) ; is file marked for delete?
	beqlu	390$			; if not, forget it
	subl3	hdlbn,rdqio+qiow$_p3,r0	; number of LBNs after previous header
	addl2	r0,hdfnum		; compute deleted file number
	movb	#^a/-/,delete		; mark the file deleted
	brb	400$			; continue processing the header
;
; block is not a header, return
;
390$:	rsb
	.page
	.sbttl	write out a header

400$:	movl	rdqio+qiow$_p3,hdlbn	; save header's LBN
	movw	hdr2$w_fseq(r9),hdfseq	; file sequence number
	movw	hdr2$w_fseg(r9),hdfseg	; file segment number
	movw	hdr2$w_efnu(r9),hdefnu	; extension file number
	movzbw	hdr2$w_ervn+1(r9),hdefnu+2 ; rest of extension file number
	movw	hdr2$w_efsq(r9),hdefsq	; extension sequence number
	movb	#^a/ /,direct		; assume it's not a directory
	bitb	#hdr2$m_scha_dir,hdr2$b_scha(r9) ; is it a directory file?
	beqlu	420$
	movb	#^a/D/,direct		; if so, mark it
420$:	movw	hdr2$w_bfnu(r9),hdbfnu	; back link file number
	movzbw	hdr2$w_brvn+1(r9),hdbfnu+2 ; rest of back link file number
	movw	hdr2$w_bfsq(r9),hdbfsq	; back link sequence number
	movaw	(r9)[r8],r8		; point to ID field
	movc3	#20,(r8),hdfnam		; copy filename
	movq	id2$q_crdt(r8),hdcrdt	; copy creation date and time
	movw	hdr2$_ufat+6(r9),hdhibk	; copy low word of high block
	movw	hdr2$_ufat+4(r9),hdhibk+2 ; copy high word of high block
	movw	hdr2$_ufat+10(r9),hdefbk ; copy low word of eof block
	movw	hdr2$_ufat+8(r9),hdefbk+2 ; copy high word of eof block
	$put	outrab			; write record
	blbs	r0,450$
	pushl	r0			; complain if write error
	calls	#1,g^lib$stop
;
; tell the operator about it
;
450$:	pushab	(r8)			; build filename string descriptor
	pushl	#20
	movaq	(sp),r0
	pushaq	(r0)			; filename string
	calls	#1,g^lib$put_output	; print filename string for operator
	addl	#8,sp			; pop descriptor off stack
	rsb

	.end	scan2
