; ; File: TFSCOMMON.a ; ; Contains: This file contains common utility routines ; ; Copyright: © 1985-1991, 1993 by Apple Computer, Inc., all rights reserved. ; ; Change History (most recent first): ; ; 8/3/93 BH Changed MarkVCB and FlushMDB to keep an eye on the ; vcbFlushCritBit flag for manual-eject drives. ; <2> 9/12/91 JSM Add a header. ; <1.2> 3/2/89 DNF removed references to forROM; HFS now builds identically for ram ; or rom ; <1.1> 11/10/88 CCH Fixed Header. ; <1.0> 11/9/88 CCH Adding to EASE. ; <1.0> 2/11/88 BBM Adding file for the first time into EASE… ; 2/19/87 BB Vectored FlushMDB. ; 11/20/85 BB FlushMDB now clears FCB dirty flag for the Extents and Catalog ; file FCBs. (ROM75 patch). ; 11/6/85 PWD Changed RoundAlloc to call DivUp by BSR.S **** changes since ; ROM75 release **** ; 10/20/85 LAK Preserve D0 in MarkVCBDirty debug code (CVFlgs was trashing it). ; 10/16/85 PWD Changed FlushMDB to not set LsMod time on dirty VCBs. ; 10/10/85 LAK Added routine MarkVCBDirty to mark VCB dirty without changing ; its mod date (special for eject). ; 10/1/85 LAK Modified GetVCBDrv to look for OffLine and Ejected volumes. ; 9/22/85 PWD Changed FlushMDB to update VCBXTAlBks and VCBCTAlBks in VCB from ; FCB information ; 8/22/85 PWD Added RoundAlloc. ; 7/11/85 PWD New today ; ;_______________________________________________________________________ ; ; External Routines: DivUp,RoundAlloc,FlushMDB ; ; Internal Routines: ; ; Called By: FlushVol,XFFlush,CMFlush ; ;_______________________________________________________________________ ;_______________________________________________________________________ ; ; DivUp subroutine: compute the minimum number of allocation blocks necessary ; to store a given number of bytes (a division followed by ; a round-up). ; ; Inputs: ; D0.L - Byte length of run on disk ; D1.W - Allocation block size ; ; Outputs: ; D0.L - Number of allocation blocks required (top word is zeroed) ;_______________________________________________________________________ ; BLANKS ON STRING ASIS DivUp: DIVU D1,D0 ; Divide by allocation blocks size SWAP D0 ; Examine the remainder TST.W D0 ; BEQ.S @10 ; If zero, we have a perfect fit. CLR.W D0 ; If not, clear the remainder SWAP D0 ; Restore the quotient ADDQ.W #1,D0 ; and add one BRA.S @20 ; @10: SWAP D0 ; Restore quotient to lower word @20: RTS ; And call it a day. ;_______________________________________________________________________ ; ; RoundAlloc: Compute the size in bytes of a byte length in allocation ; blocks, rounded up to a given clump-size boundary. ; ; Inputs: ; D0.L - Size in bytes ; D1.L - Clump size in bytes (must be 512 multiple) ; A2.L - VCB pointer (for AlBlkSiz) ; ; Outputs: ; D0.L - Rounded size in allocation blocks (always 16 bits, top word clear) ;_______________________________________________________________________ ; RoundAlloc: MOVE.L D1,-(SP) ; Save across call MOVE.L D0,-(SP) ; Save original byte length MOVE.L D1,D0 ; Set up to compute ClpSiz in AlBlks MOVE.L VCBAlBlkSiz(A2),D1 ; Pick up allocation block size in bytes BSR.S DivUp ; Divide, yielding ClpSize in AlBlks <06Nov85> MOVE.L D0,D1 ; Stash away temporarily MOVE.L (SP)+,D0 ; Now then, let's get to work MOVE.L D1,-(SP) ; Move this divisor out of the way MOVE.L VCBAlBlkSiz(A2),D1 ; Pick up alloc. block size again BSR.S DivUp ; Divide, yielding length in AlBlks <06Nov85> MOVE.L (SP)+,D1 ; Recover clump size in AlBlks again BSR.S DivUp ; Get size in Clumps <06Nov85> MULU D1,D0 ; And mult. back out -> size in AlBlks ; (guaranteed to be word magnitude) *PWD* MOVE.L VCBAlBlkSiz(A2),D1 ; Pick up alloc. block size again *PWD* MULU D1,D0 ; Get size in bytes again MOVE.L (SP)+,D1 ; Recover original D1 RTS ;_______________________________________________________________________ ; ; Routine: QWordSearch ; Arguments: A0.L (input) -- pointer to queue header ; D0.W (input) -- word parameter to look for ; D1.W (input) -- field offset for compare ; A0.L (output) -- pointer to queue element which matched ; D0.W (output) -- 0=success, -1=failure ; D1, other regs are preserved ; Called By: FindDrive,GetVCBDrv,GetVCBRfn ; Function: General queue search routine. ; ; Modification History: ; 06 Dec 82 LAK New today. ;_______________________________________________________________________ QWordSearch: MOVE.L D2,-(SP) ; save D2 MOVE.L QHead(A0),D2 ; get first element @1 BEQ.S @4 ; exit if no match MOVE.L D2,A0 ; next element pointer CMP.W 0(A0,D1),D0 ; match? BEQ.S @2 ; then take the good exit . . . MOVE.L QLink(A0),D2 BRA.S @1 @2 MOVEQ #0,D0 @3 MOVE.L (SP)+,D2 ; restore D2 TST.W D0 RTS @4 MOVEQ #-1,D0 BRA.S @3 ;_______________________________________________________________________ ; ; Routine: GetVCBRfn,GetVCBDrv,GetWDCBRfn ; Arguments: D0.W (input) -- IODrvNum(A0) = IOVRefNum(A0) (for VCB or WD) ; D0.W (output) -- error code (no such volume) or 0 ; A2.L (output) -- VCB pointer or WDCB pointer ; All other regs are preserved ; Calls: QWordSearch ; Called By: DtrmV3,MountVol(make sure no volume already mounted on a drive) ; Function: Determine VCB from DriveNum/VRefNum field. ; ; Modification History: ; 01 Jun 83 LAK New today. ; 22 Feb 85 GJC Added support for WDCB search (GetWDCBRfn). ; 28-Feb-85 PWD Changed GetWDCBRfn to store WDCB pointer in A3. ; 14-Mar-85 PWD Changed WDCB search algorithm to mimick FCB logic; WDCB's are ; allocated from a pre-allocated array a la FCBs. ; 23-Feb-85 PWD Fixed bug in GetWDCBRfn resulting from (A3)+ on CMP to D0 ; 1-May-85 PWD Added WDVCBPtr check to GetWDCBRfn. ; <01Oct85> LAK Modified GetVCBDrv to look for OffLine volumes. ; <16Oct85> PWD Changed FlushMDB to not set LsMod time on dirty VCBs ;_______________________________________________________________________ GetVCBRfn: MOVEM.L D1/A0,-(SP) ; save regs MOVEQ #VCBVRefNum,D1 LEA VCBQHdr,A0 ; search the queue of VCBs BSR.S QWordSearch BNE.S @2 MOVE.L A0,A2 ; return VCB ptr in A2 @1 MOVEM.L (SP)+,D1/A0 RTS @2 MOVEQ #NSVErr,D0 ; no such volume BRA.S @1 GetVCBDrv: MOVEM.L D1-D2,-(SP) ; save regs other than A2/D0 <01Oct85> MOVE.W D0,D1 ; save drive parameter in D1 <01Oct85> MOVE.W D0,D2 ; save negated value in D2 for <01Oct85> NEG.W D2 ; offline volume check <01Oct85> MOVE.L VCBQHdr+QHead,A2 ; get first VCB ptr @1 MOVE.L A2,D0 ; see if it's there BEQ.S @5 ; fail <01Oct85> CMP.W VCBDrvNum(A2),D1 ; this one? <01Oct85> BEQ.S @3 ; br if so <01Oct85> TST.W VCBDrvNum(A2) ; offline? <01Oct85> BNE.S @2 ; br if not <01Oct85> CMP.W VCBDRefNum(A2),D2 ; match? <01Oct85> BEQ.S @3 ; br if found <01Oct85> @2 MOVE.L QLink(A2),A2 ; go to next VCB <01Oct85> BRA.S @1 ; <01Oct85> @3 MOVEQ #0,D0 ; success! <01Oct85> @4 MOVEM.L (SP)+,D1-D2 ; restore regs <01Oct85> RTS ; <01Oct85> @5 MOVEQ #NSVErr,D0 ; no such volume <01Oct85> BRA.S @4 ; <01Oct85> ; Set up A3->WDCB, A2->VCB, D0=0 ; If error, D0=NSVErr or RFNumErr . . . GetWDCBRfn MOVEA.L WDCBsPtr,A3 ; Point to WDCB array MOVEM.L A1/D1,-(SP) ; Save A1/D1 away for use as scratch MOVEQ #0,D1 ; Clear top half of D1 MOVE.W D0,D1 ; Set up for check BSR CkWDRefNum ; Use check subroutine . (sets D0, blows A1) <29Aug85> BNE.S @1 ; Br if error <29Aug85> SUB.W #WDRfnMin,D1 ; Offset to form index into WDCB array ADDA.W D1,A3 ; Offset to point to WDCB MOVEQ #NSVErr,D0 ; Expect the worst MOVE.L WDVCBPtr(A3),D1 ; Pick up the VCB pointer BEQ.S @1 ; If clear, this WDCB was actually unused MOVEA.L D1,A2 ; Otherwise, this is a fine VCB pointer MOVEQ #0,D0 ; Indicate success @1 MOVEM.L (SP)+,A1/D1 ; Restore A1/D1 <29Aug85> TST.W D0 ; Set CCR <29Aug85> RTS ; <29Aug85> ;_______________________________________; ; ; MarkVCB utility... ; ;_______________________________________; MarkVCB: BSR.S MarkVCBDirty ; Mark the VCB dirty <18Oct85> MarkVCBTime: MOVE.L Time,VCBLsMod(A2) ; Set the time of death IF hasManEject THEN ; the mod date is critical volume info, used to identify a volume in a remount ; we need to update this change to the disk ASAP if it is in a manual-eject drive, ; since it may be removed at any time and we need to be able to tell if it is remounted ; MOVE.L D0,-(SP) ; save D0 MOVE.W vcbFlags(A2),D0 ; BTST #vcbManEjBit,D0 ; volume in a manual-eject drive? BEQ.S @noflush ; no, skip this BSET #vcbFlushCritBit,D0 ; signal need to flush the critical info MOVE.W D0,vcbFlags(A2) ; @noflush MOVE.L (SP)+,D0 ; restore D0 ENDIF ; RTS ; And return to more important things MarkVCBDirty IF HFSDebug THEN MOVE.L D0,-(SP) ; Preserve D0 <20Oct85> BSR CVFlgs ; Can we actually write this volume? <18Oct85> BEQ.S @1 ; Yes. <18Oct85> _HFSDebug $127 ; No - this MDB cannot be flushed... and you<18Oct85> ; may say to yourself "How did I get here?" <18Oct85> @1 MOVE.L (SP)+,D0 ; Restore D0 <20Oct85> ENDIF ST VCBFlags(A2) ; Mark the VCB dirty (special for eject) <10Oct85> RTS ; <10Oct85> FlushMDB: MOVE.L jFlushMDB,-(SP) ; jump table entry for vFlushMDB <19Feb87> RTS ; go there <19Feb87> vFlushMDB ; 'vectored' FlushMDB routine <19Feb87> MOVE.L (SP)+,-(A6) ; Save return address across ,ASYNC calls MOVEM.L D0-D2/A0-A1,-(A6) ; Save scratch registers ; Write out the MDB, if dirty: IF HFSDebug THEN ; *** ONLY INCLUDE WHEN DEBUGGING *** TST.W VCBDRVNUM(A2) ; Make sure the volume's on-line. BNE.S @1 DC.W $FAAA @1 ENDIF TST.B VCBFlags(A2) ; VCB Dirty? BPL FlMDBExit ; If not, don't bother writing it out MOVEA.L VCBBufAdr(A2),A1 ; Point to volume cache queue header MOVE.W VCBVRefNum(A2),D0 ; Volume refNum MOVEQ #0,D1 ; Clear flag byte ; *PWD*: Read in a fresh copy of the MDB to allow other programs to write the ; last half of the MDB and not be clobbered by our writing of the MDB: ; *LAK*: Not such a good idea. Other programs should leave our MDB alone. Let them ; use the last block on disk if they must (we don't touch it). MOVEQ #2,D2 ; MDB is block 2 (third block on disk) JSR GetBlock ; Point A0 to cache buffer BNE FlMDBExit ; Punt on errors MOVEA.L A0,A1 ; Set up as destination for _BlockMoves LEA VCBDInfoSt(A2),A0 ; Point to MDB info in VCB MOVE.L #LenMDB,D0 ; Length to copy _BlockMove ; Copy VCB info ; Add some TFS-specific information: LEA VCBTDInfoSt(A2),A0 ; Point to additional info LEA DrTInfoSt(A1),A1 ; Point to tail end of MDB MOVEQ #VCBTDILen,D0 ; Length of TFS info _BlockMove ; Copy it into the MDB Buffer SUBA.L #DrTInfoSt,A1 ; Point A1 back to start of buffer MOVEA.L FCBsPtr,A3 ; Point to FCB array MOVE.W VCBXTRef(A2),D1 ; Get extent file refNum MOVEM.L A0-A1,-(SP) ; Free up some scratch registers MOVEQ #lenXDR,D0 ; Length of extent record LEA FCBExtRec(A3,D1),A0 ; Point to extent record in FCB LEA DrXTExtRec(A1),A1 ; Point to first (and only) XT extent record _BlockMove ; Copy in the extent record MOVEM.L (SP)+,A0-A1 ; Restore originals MOVE.L FCBPLen(A3,D1.W),D0 ; Pick up length of extent B*-Tree <22Sep85> MOVE.L D0,DrXTFlSize(A1) ; Store length of extent tree in MDB buffer <22Sep85> BCLR #FCBModBit,FCBMdRByt(A3,D1.W) ; clear the FCB dirty flag <20Nov85> MOVE.L VCBAlBlkSiz(A2),D1 ; Pick up volume's allocation block size <22Sep85> JSR DivUp ; Compute extent B*-Tree size in AlBlks <22Sep85> MOVE.W D0,VCBXTAlBks(A2) ; And save it for posterity <22Sep85> MOVE.W VCBCTRef(A2),D1 ; Get catalog file RefNum MOVEM.L A0-A1,-(SP) ; Free up some scratch registers (again) MOVEQ #lenXDR,D0 ; Length of extent record LEA FCBExtRec(A3,D1),A0 ; Point to extent record in FCB LEA DrCTExtRec(A1),A1 ; Point to first (and only) XT extent record _BlockMove ; Copy in the extent record MOVEM.L (SP)+,A0-A1 ; Restore originals MOVE.L FCBPLen(A3,D1.W),D0 ; Pick up length of catalog B*-Tree <22Sep85> MOVE.L D0,DRCTFlSize(A1) ; Store length of catalog tree <22Sep85> BCLR #FCBModBit,FCBMdRByt(A3,D1.W) ; clear the FCB dirty flag <20Nov85> MOVE.L VCBAlBlkSiz(A2),D1 ; Pick up volume's allocation block size <22Sep85> JSR DivUp ; Compute allocation in alloc. blocks <22Sep85> MOVE.W D0,VCBCTAlBks(A2) ; And save the result for posterity <22Sep85> ; Write the MDB out by releasing the block dirty MOVEQ #kRBWrite,D1 ; Force it to be written . . . <01Oct85> MOVEA.L A1,A0 ; Point to MDB buffer MOVEA.L VCBBufAdr(A2),A1 ; Point to volume Cache Queue Header JSR RelBlock ; Release the cache block IF hasManEject THEN ; the critical volume info that needs to be maintained ; for volumes in manual-eject drives has been flushed. BNE.S FlMDBExit ; don't clear either dirty bit if an error occurred MOVE.W vcbFlags(A2),D0 ; do this properly...get the whole flags word BCLR #vcbFlushCritBit,D0 ; clear the correct bit MOVE.W D0,vcbFlags(A2) ; and restore the flags ENDIF; v--------------------------note naughty alternate flag-clearing method here. CLR.B VCBFlags(A2) ; VCB is not dirty any longer . . . <28Aug85> FlMDBExit MOVEM.L (A6)+,D0-D2/A0-A1 ; Restore scratch registers MOVE.L (A6)+,-(A7) ; Restore return address TST.W D0 RTS