; ; File: FileMgrHooks.a ; ; Contains: This is a grab bag of code that needs to be merged into ; the rest of the file system. ; ; Copyright: © 1990-1994 by Apple Computer, Inc., all rights reserved. ; ; Change History (most recent first): ; ; 1/29/94 DCB Roll in changes from MissingLink to support Async SCSI. ; 11/6/93 pdw Added clearing of FSCallAsync after syncWait completes to fix ; QuickTime record bug. ; 11/5/93 pdw In FSDispatchRequest, changed sne to st.b FSCallAsync to always ; call drivers asynchronously no matter what the characteristic of ; the file system request. Also rolled in the AppleShare ; 4.0-found bug fix: calling CheckSCSICollision from deferred task ; to make sure that SCSI is still free. ; 8/27/93 BH Added flushing of critical volume info for manual-eject drives ; to ExtFSHook. Some tweaks to _Eject handling for manual-eject ; drives: use A6 instead of A7 stack, bigger DSAlertRect, more ; intelligent VCB finding. ; 8/3/93 BH Added _Eject patch for manual-eject drives to ExtFSHook. ; 10/22/92 CSS Change some branch short instructions to branches. ; 4/1/92 kc Copied the code from FileMgrPatches.a that needs ; to be rolled in at a later date. ; ; To Do: ; Merge/Roll this into the other sources ; ;ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ ;________________________________________________________________________________ ; ; FSQueueHook patch <9> ; ; Grab control at the FSQueueHook to implement new call dispatching and some ; patches after the SyncWait loop. ; ; We now defer dispatching a call if either the SCSI bus is busy or the interrupt ; mask is up. To implement the SCSI deferral we wait until the SCSI manager calls ; us through the jSCSIFreeHook when the bus frees up. If the interrupt mask is up, ; we install a deferred task. ; ; After the SyncWait loop (i.e. only at the completion of synchronous calls) we ; tag FCBs with the serial number of the process that's opening them, and we ; attempt to allocate more FCBs or WDCBs if we run out. We also do the disk recovery ; code (disk switch). ; ; Inputs: ; d0.w dispatch selector if $ax60 call ; d1.w trap word ; a0.l caller's pb ; ; ASSERT: this patch must be the first one hooked in so that it is the ; last one called. It must be the last one called because we are replacing ; the code in rom that follows the call to the fsQueueHook. ;ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ fsQueueHookPatch: proc export export vAfterFSQHook, FSDispatchRequest, CheckInterruptMask import MungeTrapWord,UnMungeTrapWord, fsInterruptDeferProc import TagFileWithPSN, CheckFCBAllocation, MoreWDCBs VolatileRegs reg a0-a1/d1-d2 pascalRegs reg a2-a6/d3-d7 addq.l #4,sp ; we never want to return to ROM vAfterFSQHook: move.w #1,IOResult(a0) ; set IOResult to "in process" bsr MungeTrapWord move.l (sp)+,IOCmdAddr(a0) ; save address to go to when ready move.w #FSQType,IOType(a0) ; say its a file system queueing element btst #AsyncTrpBit,d1 ; async bit on? beq.s notAsync ; br if not tst.b FrcSync ; force it synchronous? beq FSAsync ; if not, do it async bra.s doItSync notAsync: clr.l IOCompletion(a0) ; no completion routines for sync calls doItSync: move.l IOCmdAddr(a0), -(sp) ; save address to go to when ready lea SyncReadyToRun, A1 move.l A1, IOCmdAddr(a0) ; replace IOCmdAddr with our intermediate call move.l A0, -(sp) ; save parameter block ptr bsr FSAsync ; queue it up (and maybe call it) move.l (sp)+, A0 syncStartWait: tst.l IOCmdAddr(A0) bne.s syncStartWait runSync: move.l (sp)+, A1 ; get address to call movem.l A0/PascalRegs, -(sp) ; observe Pascal regsave conventions move.l A1, IOCmdAddr(a0) ; restore processing routine addr movea.l PMSPPtr, A6 ; Point to the Poor Man's Search Path table clr.w PMSPIndx(A6) ; No entries used yet movea.l HFSStkTop, A6 ; Set up A6 for use as stack pointer jsr (a1) ; go do the call movem.l (sp)+, A0/PascalRegs ; restore registers ; synchronous calls spin here until call is complete vSyncWait: SyncWait: move.w ioResult(a0),d0 ; get the result code into d0 bgt.s SyncWait ; done when result is zero or negative ; We're done with a synchronous call. See what work there is to do. ; 1) on successful file open, tag the file with the current process ID ; 2) on failure of any call, see if recovery (fcb's, wdcb's, offline) is possible tst.w d0 ; call successful? bne.s @ErrorRecovery ; if not, try recovering ; see if this is an open. Tag the FCB with the current process ID for _FSCleanup bsr UnMungeTrapWord ; get trap word in d1, selector in d0 andi.w #$f0ff,d1 ; clear the modifier bits cmp.w #$a060,d1 ; is it our dispatcher? bne.s @CheckOpenTraps ; if not, look at straight traps cmp.w #selectOpenDF,d0 ; is it _OpenDF? beq.s @TagIt ; branch if so cmp.w #selectOpenDeny,d0 ; is it _OpenDeny? beq.s @TagIt ; branch if so cmp.w #selectOpenRFDeny,d0 ; is it _OpenRFDeny? beq.s @TagIt ; branch if so @Return: move.w ioResult(a0), d0 ; restore the result code to d0 ext.l d0 ; make it long for tradition's sake rts ; return to caller @CheckOpenTraps cmp.w #$a000,d1 ; is this _Open? beq.s @TagIt ; branch if so cmp.w #$a00a,d1 ; is this _OpenRF? bne.s @Return ; branch if not an open of any kind @TagIt bsr TagFileWithPSN ; put PSN in parallel array (a0 = iopb) bra.s @Return ; common exit ; try to recover if a synchronous call failed because FCB or WD array was full @ErrorRecovery cmp.w #TMFOErr,d0 ; Run out of FCBs? bne.s @CheckWDCBs ; Make sure we ran out of FCBs locally <38> movea.l FSVarsPtr,a1 ; <38> btst.b #fsNeedFCBs,FSVars.fsFlags(a1) ; Too few FCBs locally? <38> beq.s @CheckWDCBs ; no? check for WDCB space <38> bsr CheckFCBAllocation ; see what we can do about the FCB trouble <38> beq.s @RetryCall ; we can try again if we got more FCBs <38> @CheckWDCBs: cmp.w #TMWDOErr,d0 ; Run out of WDCB space? bne @Continue ; no? then I don' wanna talk to you bsr MoreWDCBs ; try to allocate more WDCBs bne.s @Continue ; Errors? Then, my dear, we've failed @RetryCall: ; <15> bsr UnMungeTrapWord ; trap word in d1, dispatch in d0 lea.l TrapAgain,a1 ; get ready to play assembler move.w d1,(a1)+ ; write in the trap word move.w #$4E75,(a1) ; follow it with an RTS move.l jCacheFlush,a1 ; get address of the CPUΥs cache flusher <45> jsr (a1) ; be cool on hoopy cached cpus <45> jmp TrapAgain ; Go re-execute the original trap ; call disk switch hook if synchronous call failed because volume went offline @Continue: ext.l d0 ; extend result to long cmp.w #VolOffLinErr,d0 ; volume off-line error? (detected by file bne.s @Return ; system) br if not move.l DskSwtchHook,-(sp) ; OffLineVol points to VCB, a0 to request bgt.s @Return ; br if there is a hook proc addq #4,sp ; otherwise, just return ;₯ This code was not in the system patch do we want it?? ;₯ IF ForROM THEN ;₯ btst #7, DSAlertRect ; already in our proc? ;₯ bne DSHook ; br if not ;₯ ENDIF bra.s @Return ; join exit code FSAsync: move sr, -(sp) ; save interrupt state ori #HiIntMask, sr ; turn interrupts off lea.l FSQHdr,a1 ; Point to the File System Queue _Enqueue ; Enqueue the request bset.b #fsBusyBit, FSBusy ; make sure the file system is marked busy beq.s @1 ; if free, go try to dispatch the call move.w (sp)+, sr ; restore interrupt state moveq.l #noErr, d0 ; no errors (yet) rts @1: move.w (sp)+, sr ; restore interrupt state ; Now we have to worry about whether interrupts are masked out. We don't want to be ; running long file system calls with interrupts masked. If interrupts are masked, we'll ; install a deferred task that will restart us when the mask is back down. ; Compatibility note: Macsbug likes to do logging to files with interrupts off. While ; we can't guarantee that this will work forever, it was the general consensus that it was ; worth letting sync calls go through even if the interrupt mask was > 0 so as to let ; macsbug do logging. This also protects a few skanky programs which do sync calls from ; vbl tasks and the like, although it doesn't change the fact that sync calls from interrupt ; level are completely illegal. ; Note that we should probably put similar logic into the completion routines at ; Basic I/O, since they are called as the result of interrupts. This hasn't proven to be ; a problem, though. CheckInterruptMask: move.l a0, -(sp) ; save a0 <34> move.l FSQHead,a0 ; get first parameter block <34> btst.b #AsyncTrpBit, ioTrap(a0) ; is this a sync call? <34> movea.l (sp)+, a0 ; restore a0 (keep ccr) <34> beq.s SavePascalRegistersAndDispatch ; if sync, don't worry about interrupts <34> move.w sr, d0 ; get interrupt state andi.w #$0700,d0 ; interrupt mask > level 0? beq.s SavePascalRegistersAndDispatch ; If not, there's nothing to worry about ; The mask is up - install a deferred task movem.l VolatileRegs, -(sp) ; save across _DTInstall move.l FSVarsPtr, a1 ; point to file system lomem bset.b #fsIntMaskDefer, FSBusy ; do we already have one in the oven? bne.s @AlreadyInstalled ; if yes, we can go now lea.l FSVars.fsDefer(a1), a0 ; aim at our deferred task pb lea fsInterruptDeferProc, a1 ; aim at our deferred task proc move.l a1, dtAddr(a0) ; tell the pb about it move.w #dtQType,qType(a0) ; Indicate proper queue type clr.l dtResrvd(a0) ; 'cause they told me to clr.w dtFlags(a0) ; 'cause it's reserved _DTInstall ; call 976-wake @AlreadyInstalled: movem.l (sp)+, VolatileRegs ; restore registers rts ; <48> ; SavePascalRegistersAndDispatch - the general call dispatcher. ; We make the following assumptions: ; We're not going to collide with a SCSI transaction ; We don't need to defer for anything (interrupts, etc.) ; The file system busy flag has been set ; SavePascalRegistersAndDispatch: movem.l PascalRegs,-(sp) ; observe Pascal regsave conventions bsr FSDispatchRequest ; do the work movem.l (sp)+,PascalRegs ; restore registers rts ; and return (all commands finish by jumping to CmdDone) ; FSDispatchRequest - the specific call dispatcher ; We assume that the appropriate registers have been saved and dispatch the call. FSDispatchRequest: move.l FSQHead,a0 ; get first parameter block move.l IOCmdAddr(a0),a1 ; get address to call movea.l PMSPPtr,a6 ; Point to the Poor Man's Search Path table clr.w PMSPIndx(a6) ; No entries used yet btst.b #AsyncTrpBit,ioTrap(a0) ; set the flavor for this trap <01Oct85> sne.b FSCallAsync ; if sync, we'll do our I/O synchronous <01Oct85> movea.l HFSStkTop,a6 ; Set up A6 for use as stack pointer jmp (a1) ; go do the call <48> ;ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ ; ; This is our signal routine which is called when the sync IO is initially dispatched. ; It signals the syncStartWait loop by clearing the IOCmdAddr field. ; SyncReadyToRun clr.l IOCmdAddr(A0) rts endproc ;_____________________________________________________________________________________ ; <38> ; CheckFCBAllocation ; ; Function: ; Check to see if there's a pending request for more FCBs. If ; there is a request, service it. ; ; This routine is called from both the tail of the syncwait loop and ; from idle time. ; ; Note that we only attempt to allocate more FCBs when the #fsNeedsFCBs ; bit is set. This allows the syncwait loop to come here for all ; #tmfoErr cases, even when those cases were generated by remote ; volumes failing to get FCBs. ; ; Note that this routine makes a file system call (_AllocateFCBs), so ; it can't be called from within the file system. ; ; Input: ; none ; ; Output: ; d0.w - #noErr if nobody has any outstanding FCB requests or ; the outstanding request was successfully fulfilled ; ; - #tmfoErr if FCBs were requested and none could be made ; available ; ; ccr's - tst.w of d0 ;_____________________________________________________________________________________ CheckFCBAllocation: proc @regs reg d1/a0/a1 movem.l @regs,-(sp) move.l FSVarsPtr,a1 with FSVars btst.b #fsNoFCBExpansion,fsFlags(a1) ; does anybody even care? bne.s @quickExit bclr.b #fsNeedFCBs,fsFlags(a1) ; does anybody need any FCBs? beq.s @quickExit ; if not, cruise sub.w #ioHQElSize,sp ; allocate an iopb movea.l sp,a0 ; grab the iopb pointer move.w fsFCBBurst(a1),ioFCBBurst(a0) ; indicate desired FCB headroom move.w fsFCBGrow(a1),ioFCBGrow(a0) ; indicate target when we do grow _AllocateFCBs ; go see what we can get move.w ioTotalFCBs(a0),d1 ; grab the total now add.w #ioHQElSize,sp ; deallocate iopb cmp.w fsFCBMax(a1),d1 ; are we at max FCBs? blt.s @exit ; if not, don't worry bset.b #fsNoFCBExpansion,fsFlags(a1) ; if so, don't bother keeping track anymore @exit: movem.l (sp)+,@regs tst.w d0 rts @quickExit: moveq.l #noErr,d0 bra.s @exit endwith endproc ;_____________________________________________________________________________________ ; ; FileManagerIdleProc ; ; Function: ; This function is called from _IdleSynchTime to allow the file system ; to perform periodic activity. ; ; Currently we check to make sure that enough FCBs are free. ; ; Input: ; Output: ; none ;_____________________________________________________________________________________ FileManagerIdleProc: proc export jsr CheckFCBAllocation rts endproc ;ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ ;________________________________________________________________________________ ; ; Routine: MoreFCBs ; Function: Create more FCBs. ; Input: d2.w - number of FCBs to extend array by <38> ; ; Output: d0 = OSErr. #noErr on success. #TMFOErr on failure. ; condition codes = (tst.w d0) ; ; This routine is called just after syncWait time (obviously it only works on synchronous ; calls) where a #TMFOErr is seen. Attempt to allocate a new, larger FCB array and copy the ; contents of the old FCB array to the new area and release the old FCB array. ; NOTE: We thought of just SetPtrSize'ing the old main array to create the new, larger parallel ; array. This has a couple problems: a) the parallel array elements may some day be ; bigger than the main elements, and b) more obtusely, we deal better with ; fragmentation by doing a NewPtr, since it may find a better spot than the current one. ; ; This routine is also called by our FS idle proc when it decides that there ; aren't enough FCBs lying around. ;________________________________________________________________________________ FCBGrowRegs reg d1-d4/a0-a3 MoreFCBs: proc movem.l FCBGrowRegs,-(sp) ; Save some scratch regs ; Remember # of FCBs that we're attempting to add move.w d2, d4 ; Calculate size of new, larger main FCB array and attempt to allocate it move.l FCBsPtr,a2 ; Point to existing FCB array moveq.l #0,d0 ; clear d0 to receive long size move.w (a2),d0 ; get size of the array in bytes move.w FSFCBLen,d1 ; start with the length of a single fcb mulu.w d4,d1 ; multiply by the number we're adding <38> add.w d1,d0 ; add size to extend it by bmi @FailedMain ; if d0 > maxSignedWord we're stuck move.l d0,d3 ; save new size (for switching sizes later) _NewPtr sys, clear ; ...and try to allocate a larger array bne @FailedMain ; Xfer if can't get the memory... ; Copy contents of old FCB array to the newly allocated larger, main FCB array ; Gentle Reader: BlockMove'ing the old to the new, of course, copies the size field as ; well as the data. We postpone setting the new size, tho, until we know we have a new ; block for the enlarged parallel array. This keeps asynch callers from tripping over ; inconsistencies, without requiring us to turn ints off across the NewPtr. Also ; makes error recovery from second alloc a simple SetPtrSize of the first alloc. move.l a0,a1 ; Dest address is new FCB array movea.l a2,a0 ; Source is old FCB array moveq.l #0, d0 ; clear the high bytes move.w (a2),d0 ; Get size of the old array move.w sr,-(sp) ; save current status register ori.w #HiIntMask,sr ; turn interrupts off to protect from marauding async calls _BlockMove ; copy the old array (including its old size word) move.l a1,FCBsPtr ; set new FCB array address (leave the old size for now) move.w (sp)+,sr ; restore status register ; Deallocate the old FCB array (fill memory with bus error bait to catch stale users) move.l a2,a0 ; pointer to old FCB array for disposal moveq.l #0, d0 ; clear the high bytes move.w (a2),d0 ; get size of the old array lsr.l #1,d0 ; convert to words subq.l #1,d0 ; adjust for dbra @CrudLoop: move.w #$fe1d,(a2)+ ; fill a word and inc pointer dbra d0,@CrudLoop ; decrement count and loop if non-zero _DisposPtr ; release the block ; now replace the parallel FCB array with a larger one move.l FSVarsPtr,a3 ; a3 = ptr(FSVars) movea.l FSVars.fcbPBuf(a3),a2 ; a2 = current FCB parallel array move.w cbPBufULen(a2),d0 ; size of each parallel array element move.w cbPBufCount(a2),d1 ; count of existing array entries mulu.w d0,d1 ; size of elements in existing array addq.l #fcbPBufData,d1 ; d1 = size of current array and header info mulu.w d4,d0 ; calc bytes of additional elements <38> add.l d1,d0 ; d0 = new array size _NewPtr sys, clear ; allocate (bound to work since we just freed old main array) bne.s @FailedParallel ; deal with failure, tho ; We have all we need. Copy old to new, update the count in the parallel array, and ; the size in main array. This is a critical section because a) we can't afford someone ; modifying part of the old array after BlockMove has passed it, and b) the main size ; and the parallel count need to agree. We disable interrrupts. movea.l a0,a1 ; a1= destination=new array movea.l a2,a0 ; a0= source =old array move.l d1,d0 ; d0= count =old size move.w sr,-(sp) ; save current status register ori.w #HiIntMask,sr ; turn interrupts off to protect from marauding async calls _BlockMove ; copy over add.w d4,cbPBufCount(a1) ; set new count <38> move.l a1,FSVars.fcbPBuf(a3) ; save new array movea.l FCBsPtr,a0 ; now that it is safe, update the main FCB array size move.w d3,(a0) ; use the d3 we made long ago move.w (sp)+,sr ; restore status register ; free the old parallel array movea.l a2,a0 ; Pointer to old parallel FCB array _DisposPtr ; Release it moveq #noErr,d0 ; say noErr since it worked! @ExitOutNow movem.l (sp)+,FCBGrowRegs ; restore the scratch regs rts ; We got the memory for the larger main FCB array but failed to get the memory for ; the parallel FCB array. However, anticipating this problem, we haven't ; yet set the size word in the new FCB array. Since we're failing to ; grow, we'll just set the pointer size of the (new, bigger) FCB array ; back to the size it used to be and report the #TMFOErr to the caller. @FailedParallel: movea.l FCBSPtr,a0 ; point to the base of the FCB array moveq.l #0,d0 ; clear since SetPtrSize takes a long move.w (a0),d0 ; get size of old array _SetPtrSize ; drop the size back down @FailedMain: moveq.l #TMFOErr,d0 ; we have to live with it bra.s @ExitOutNow ; share exit endproc ;________________________________________________________________________________ ; ; Routine: MoreWDCBs ; Function: Create more WDCBs. ; Input: none. ; Output: d0 = OSErr. #noErr on success. #TMWDOErr on failure. ; condition codes = (tst.w d0) ; ; This routine is called just after syncWait time (obviously it only works on synchronous ; calls) where a #TMWDOErr is seen. Attempt to allocate a new, larger WDCB array and copy the ; contents of the old WDCB array to the new area and release the old WDCB array. ; NOTE: We have no choice but to use a hardwired constant (WDCBLen) for the WDCB element ; size, since the value is not stored in memory. ; NOTE: We thought of just SetPtrSize'ing the old main array to create the new, larger parallel ; array. This has a couple problems: a) the parallel array elements may some day be ; bigger than the main elements, and b) more obtusely, we deal better with ; fragmentation by doing a NewPtr, since it may find a better spot than the current one. ;________________________________________________________________________________ entry MoreWDCBs WDCBGrowRegs reg d1-d3/a0-a3 MoreWDCBs proc movem.l WDCBGrowRegs,-(sp) ; Save some scratch regs ; calculate larger size, and try to allocate the memory move.l WDCBsPtr,a0 ; point to existing WDCB array moveq.l #0, d0 ; clear the high bytes move.w (a0),d0 ; get size of that array (bytes) addi.w #fsWDCBExtendCount*WDCBLen,d0 ; calc the new, larger size <38> bmi @FailedMain ; if d0 > maxSignedWord we're stuck CSS move.l d0,d3 ; save new size (for switching sizes later) _NewPtr sys, clear ; ...and try to allocate a larger array bne @FailedMain ; jump if can't get the memory... movea.l a0,a1 ; copy ptr to useful and durable register ; copy contents of old WDCB array to the new location ; Gentle Reader: BlockMove'ing the old to the new, of course, copies the size field as ; well as the data. We postpone setting the new size, tho, until we know we have a new ; block for the enlarged parallel array. This keeps asynch callers from tripping over ; inconsistencies, without requiring us to turn ints off across the NewPtr. Also ; makes error recovery from second alloc a simple SetPtrSize of the first alloc. movea.l WDCBsPtr,a0 ; source is existing WDCB array moveq.l #0, d0 ; clear the high bytes move.w (a0),d0 ; get size of old array (bytes) movea.l a0,a2 ; save for later move.w sr,-(sp) ; save current status register ori.w #HiIntMask,sr ; turn interrupts off to protect from marauding async calls _BlockMove ; a1 is preserved move.l a1,WDCBsPtr ; set new WDCB array address move.w (sp)+,sr ; restore status register ; deallocate the old WDCB array move.l a2,a0 ; Pointer to old WDCB array _DisposPtr ; Release it ; now replace the parallel WDCB array with a larger one move.l FSVarsPtr,a3 ; a3 = ptr(FSVars) movea.l FSVars.wdcbPBuf(a3),a2 ; a2=old WDCB parallel array move.w cbPBufULen(a2),d0 ; d0=unit size move.w cbPBufCount(a2),d1 ; old array count mulu.w d0,d1 ; calc bytes of array elements addq.l #wdcbPBufData,d1 ; d1=current buffer size, including header mulu.w #fsWDCBExtendCount,d0 ; calc bytes of additional elements <38> add.l d1,d0 ; d0=new array size _NewPtr sys, clear ; ...and try to allocate a larger array bne.s @FailedParallel ; deal with error ; We have all we need. Copy old to new, update the count in the parallel array, and ; the size in main array. This is a critical section because a) we can't afford someone ; modifying part of the old array after BlockMove has passed it, and b) the main size ; and the parallel count need to agree. We disable interrrupts. movea.l a0,a1 ; a1=destination=new array movea.l a2,a0 ; a0=source=old array move.l d1,d0 ; old size move.w sr,-(sp) ; save current status register ori.w #HiIntMask,sr ; turn interrupts off to protect from marauding async calls _BlockMove ; copy over add.w #fsWDCBExtendCount,cbPBufCount(a1) ; set new count <38> move.l a1,FSVars.wdcbPBuf(a3) ; save new array movea.l WDCBsPtr,a0 ; now that it is safe, update the main WDCB array size move.w d3,(a0) ; use the d3 we made long ago move.w (sp)+,sr ; restore status register ; free the old parallel array movea.l a2,a0 ; Pointer to old WDCB array _DisposPtr ; Release it moveq #noErr,d0 ; say noErr since it worked! @ExitOutNow movem.l (sp)+,WDCBGrowRegs ; restore the scratch regs rts ; We got the memory for the larger main WDCB array but failed to get the memory for ; the parallel WDCB array. However, anticipating this problem, we haven't ; yet set the size word in the new WDCB array. Since we're failing to ; grow, we'll just set the pointer size of the (new, bigger) WDCB array ; back to the size it used to be and report the #TMWDOErr to the caller. @FailedParallel: movea.l WDCBsPtr,a0 ; point to the base of the WDCB array moveq.l #0,d0 ; clear since SetPtrSize takes a long move.w (a0),d0 ; get size of old array _SetPtrSize ; drop the size back down @FailedMain: moveq.l #TMWDOErr,d0 ; we have to live with it bra.s @ExitOutNow ; share exit endproc ;________________________________________________________________________________ ; ; fsInterruptDeferProc ; ; This procedure is the completion routine of the file system's deferred ; task. It clears the fsIntMaskDefer flag and jumps into dispatching the frontmost ; call. ; ;________________________________________________________________________________ fsInterruptDeferProc: proc export import FSDispatchRequest, CallWithRegistersPreserved interruptRegs reg a4-a6/d4-d7 bclr.b #fsIntMaskDefer, FSBusy ; indicate that there's no task pending movem.l InterruptRegs,-(sp) jsr FSDispatchRequest ; call dispatcher movem.l (sp)+,InterruptRegs rts endproc ;ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ ; ; fsSCSIFreeHookProc ; ; This procedure is called by the SCSI manager every time the SCSI bus is ; freed up. If (the File System is busy) and (we're waiting for a wake up) ; then jump to where we deferred until SCSI was free. ; ;ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ fsSCSIFreeHookProc: proc import SCSINotBusy bclr.b #fsSCSIDefer, FSBusy ; if we're waiting, we'll have this flag set beq.s NoWakeUp MOVEM.L D1-D7/A0-A6,-(SP) ; preserve registers (for caller of fsSCSIFreeHook) PEA SCSIBusyReturn ; return to here to restore regs MOVEA.L HFSStkPtr,A6 ; Recover HFS' private stack pointer MOVEM.L (A6)+,D1-D7/A0-A5 ; Retrieve registers off A6 stack MOVE.L A6,HFSStkPtr ; save stack ptr JMP SCSINotBusy ; continue with the dispatch SCSIBusyReturn MOVEM.L (SP)+,D1-D7/A0-A6 ; restore significant registers NoWakeUp RTS ; endproc ;________________________________________________________________________________ ; ; fsGeneralWakeUp ; ; This procedure can be called by anyone at any time. It checks to see if there ; is any File System work that needs to be done, and if so, dispatches it. ; ;________________________________________________________________________________ fsGeneralWakeUp: proc import CheckInterruptMask move sr,-(sp) ; save interrupt state ori #HiIntMask,sr ; only debug interrupts allowed tst.l FSQHead ; is a command pending? beq.s @Done bset.b #fsBusyBit, FSBusy ; make sure the file system is marked busy bne.s @Done ; if it was already busy, we can return move.w (sp)+,sr ; restore interrupt state bra CheckInterruptMask ; go run the next call @Done: move.w (sp)+,sr ; restore interrupt state and return rts ;________________________________________________________________________________ ; Routine: ProcessMgrExists ; Function: check if Process Mgr is running ; Input: none ; Output: D0=0 if it is ready; all other registers are preserved. ; Condition codes = (tst.w d0) ; ________________________________________________________________________________ ProcessMgrExists proc export move.l a0,-(sp) ; save a0 move.l #GestaltOSAttr,d0 ; set up Gestalt selector _Gestalt ; ask Gestalt about Process Mgr movea.l (sp)+,a0 ; d0 = OSErr, noErr if Process Mgr exists rts ; leave with condition codes intact endproc ;________________________________________________________________________________ ; ; Routine: TagFileWithPSN ; ; Function: Store current process ID in parallel array element for the file ; whose refnum is in the iopb passed in a0. ; ; Inputs: A0 = Parameter block ; Output: A0 = Parameter block ; Eats: pascal regs, except a0 ; ;________________________________________________________________________________ proc entry TagFileWithPSN TagFileWithPSN ; Open traps can be (and are!) called before Process Mgr is init'd bsr ProcessMgrExists ; is Process Mgr ready? bne.s @CantRecord ; Process Mgr is not ready yet ; find the current PSN move.l a0,-(sp) ; save pb pointer on stack suba.w #10, sp ; allocate storage for PSN (8 bytes) and result pea 2(sp) ; push address of PSN storage _GetSystemClientProcess ; get PSN with which to associate the file tst.w (sp)+ ; ignore result (on error, PSN == kNoProcess) ; PSN on top of stack. Locate entry in parallel array, and store. movea.l 8(sp),a0 ; get iopb back in a register move.w ioRefNum(a0),d0 ; get refNum to convert it bsr ParallelFCBFromRefnum ; a1 = ptr(parallel FCB entry) move.l (sp)+,xFCBPid1(a1) ; save high half of PSN move.l (sp)+,xFCBPid2(a1) ; save low half of PSN tst.l (sp)+ ; pop a0 (since we've already restored it) @CantRecord rts endproc ;________________________________________________________________________________ ; ; ExtFSCatchAll - catch external file system calls that nobody handled ; ; To complete our ability to implement future file system calls in external ; file systems before we implement them locally, we pass unknown a060 ; traps (i.e. those that we dispatch to UnknownCall) down the toExtFS hook ; to see if there are any external file systems that want to process them. ; ; If an external file system does process the call it will change the error ; code to something negative or zero. If nobody handles it, we'll arrive ; here with a positive dispatch number in d0. ; #paramErr is for calls that are known now but not implemented locally ; #extFSErr is for calls which we do implement, so if we're here then ; nobody spoke up for the call. ; ; Note that we don't have to worry about straight traps ending up here, since ; if they get dropped on the floor we'll exit the toExtFS hook with the ; original #ExtFSErr still sitting in d0. ; ; Inputs: ; a0 - pointer to currently executing parameter block ; d0 - dispatch selector ; ; Currently, we send the following dispatch values through UnknownCall ; $1c - $1f we want to be flexible about the parameters to these ; selectVolumeMount ($41) since there's no volume to aim at ; selectGetUGEntry ($44) - $4f ₯₯ should get the hell off of $a060, that's what. ; everything above $70 we want to be flexible about the parameters to these ;________________________________________________________________________________ ExtFSCatchAll: proc tst.w d0 ; is this a selector? <15Jan91 #28> ble.s @Exit ; no <15Jan91 #28> cmp.b #$1c, d0 ; check the bottom of the first range blo.s @ExtFSErrExit ; since we implement dispatches < $1c, return #ExtFSErr cmp.b #$1f, d0 ; check the top of the first range bls.s @ParamErrExit ; we don't implement $1c<=dispatch<=$1f, so #paramErr it cmp.b #selectVolumeMount, d0 ; check for volumeMount beq.s @ParamErrExit ; no local volumeMount, so #paramErr it cmp.b #selectGetUGEntry, d0 ; check for bottom of third range blo.s @ExtFSErrExit cmp.b #$4f, d0 ; check top of third range bls.s @ParamErrExit cmp.b #$70, d0 ; check for the edge of the known world blo.s @ExtFSErrExit @ParamErrExit: moveq.l #paramErr, d0 bra.s @Exit @ExtFSErrExit: moveq.l #extFSErr, d0 ; restore correct error code @Exit: rts endproc ;_____________________________________________________________________________________ ; ; MungeTrapWord (d0 and d1) -> ioTrap(a0) ; ; Function ; Store the trap word in d1 into ioTrap(a0). If the trap is Ax60, combine ; d1 with the dispatch value on d0 before storing. ; ; Input: ; a0 - pointer to caller's PB ; d0 - dispatch value (for Ax60 traps) ; d1 - trap word ; ; Output: ; a0 - pointer to caller's PB ; ioTrap(a0) - munged trap word ; ; All registers preserved ;_____________________________________________________________________________________ MungeTrapWord: proc export move.w d1,IOTrap(a0) ; save the trap number cmp.b #$60,d1 ; TFS trap dispatch? bne.s @noNeedToMunge ; If not, leave ioTrap alone and.b #$0F,ioTrap(a0) ; Leave only modifier bits move.b d0,ioTrap+1(a0) ; Store trap index in low byte @NoNeedToMunge: rts endproc ;_____________________________________________________________________________________ ; ; UnMungeTrapWord ioTrap(a0) -> d0 and d1 ; ; Function ; Expand the trap word in ioTrap(a0) into its original dispatch trap ; word (in d1) and leave the selector in d0. ioTrap is not touched. ; ; Note that we're not converting the dispatch selector into a signed byte. ; The negative value we return is a word, so it's cool to check the word ; in d0 for negativeness even when we get dispatch selectors > 128. ; ExtFSErr happens to be the right value when calling toExtFS, so it's the ; negative number of choice. ; ; Input: ; a0 - pointer to caller's PB ; ; Output: ; a0 - pointer to caller's PB ; d0.w - dispatch value (for dispatch traps), or #ExtFSErr (for straight traps) ; d1.w - trap word ; N - set for straight traps, clear for $Ax60 traps ; ; Note: ; This routine is called by the desktop manager, too. ;_____________________________________________________________________________________ UnMungeTrapWord: proc export moveq.l #ExtFSErr,d0 ; for straight traps move.w ioTrap(a0),d1 ; get munged version of trap word bmi.s @Exit ; no need to un-munge if it was an Ax00 trap moveq.l #0,d0 ; clear long since we only move a byte move.b d1,d0 ; d0 = hfs dispatch code clr.b d1 ; clear selector from low byte ori.w #$a060,d1 ; merge HFSDispatch trap word with modifiers @Exit: tst.w d0 ; sets N for straight traps rts endproc ;________________________________________________________________________________ ; ; ExtFSHook patch <9> ; ; Grab control at the ExtFSHook to implement patches to CmdDone. We are ; called after the file system has executed the local attempt at the call ; and after it has attempted the PMSP. ; ; We are currently performing the following steps ; ; 1) Run external file systems if necessary (replaces code in ROM) ; 2) Do check to catch stray selectors ; 3) Perform MakeFSSpec emulation if necessary ; 4) Set up the info for the disk switch hook ; 5) UnTag FCBs on all _Close calls ; 6) Fix up FCBs on successful _OpenRF calls (details follow) ; 7) Call completion routines (replaces code in ROM) ; 8) Check for more calls to dispatch (replaces code in ROM) ; ; Calling external file systems is tricky since the AppleShare external file ; system (and any other peer-to-peer client file systems) dequeue their calls ; and clear the local file system (to allow peers to send calls to this machine ; and not get stuck in a deadlock). ; The rules of calling the external file system chain allow for a single ; address on top of the stack that the external file system should return ; to when the call is really complete. Below that the stack belongs to ; whomever we/the external file system plans to return to during async execution ; of the call (quite possibly a return address into the file system syncwait loop, ; but we can't be sure). ; ; Figure 1: ; ; Top of a7 stack ; The Command Done Address External file system pop this and jump here when done ; The Async Context External file systems rts when returning asynchronously ; (arbitrarily deep) ; Bottom of a7 stack ; ; Since we can save at most one return addresses' worth of context (the one that the ; external file system will return to when it is done with the call) AND we ; (this patch) are jsr'ed to from the rom, we would have no way to calling external ; file systems with the right stack depth. So, we throw away the return address to ; ROM and replace all of the code that the ROM normally does. ; ; Nota Bene: ; Anyone who hooks onto the ExtFSHook and bsrOlds is guaranteed to break the ; system since we required the ability to call external file systems from ; this patch and calling external file systems requires the above-mentioned ; stack discipline. ; ; All of this stack business is why the patches are all at the same subroutine level. ; Obviously, you can't write a patch that will call external file systems if you ; are down in a subroutine. ; ; See tech note #xxxxx (the exciting tech note of the future about those wacky zany ; file system hooks) ; ; The dirty details about _OpenRF and AdjEof ; ; The logic at the end of FOpen (the common data and resource fork opener) has ; a bug in the code when it syncs up the FCBs of the fork it just opened (which ; has information from the catalog file record) with any other FCBs (which ; have information that's been modified by file system calls ; made since the last flush). The code loops through all FCBs looking ; for forks on the same volume with the same file number of the same rsrc/data ; type that aren't the file that was just opened. If it finds one, it calls the ; AdjEOF subroutine, which forces all FCBs of the same file/fork to have the ; the same file positioning information. The bug is that the check for "same ; fork" is flawed. It loads D3 with a WORD sized value when extracting the ; FCBMdRByt field from the new FCB, but uses it as a byte when comparing it to ; the FCBMdRByt field of other FCBs. This means it is using the byte after ; FCBMdRByt, which is FCBTypByte. FCBTypByte happens to always be zero in HFS ; (type is an MFS concept). A zero in the fork flag () of FCBTypByte means ; "data fork", so data fork opens act correctly. Of course, resource fork opens ; do not. We make up for it in this patch to OpenRF. ; ; ₯₯ What about the fact that a resource file will always look like a data ; fork to the ROM's confused FCB synchronizer? ; ; Inputs: ; d0.w - result code from current file manager call input to toExtFS hook ; d1.l - (on Open calls only) points to a free fcb input to toExtFS hook ; a0.l - caller's pb input to toExtFS hook ; a3.l - possible pointer to WDCB input to toExtFS hook ; ; Register usage: ; We can trash only interrupt regs (d0-d3/a0-a3). ; ; According to the unpublished IM Vol. IV chap (21), external file systems ; can trash only d1/d2/a0/a1 ; ; But, old versions of XFS (CD-ROM, ISO-9660) trash D4, HFSStkPtr and any other ; side effects of calling the disk cache (though it points a6 at its own ; space). ; ; As far as we know, none of the external file systems use the memory ; below HFSStkTop^ for their own use. ; ; When you enter command done, registers d0-d3/a0-a3 are protected and ; are available for File System use. d0/d1/a0/a3 are input parameters to ; the external file system chain. ; ; About the #NoMacDskErr bug: We assume that it is fixed in a patch that is ; called before this patch is. ; ; Reentrancy: ; The AppleShare client file system causes this code to be reentered. When ; AppleShare decides to derail a call (because it is heading to an external ; volume that might be on a peer of this machine) it remembers the return ; address (as it should) but removes the PB from the front of the FSQueue ; and clears the FSBusy word (which it really shouldn't). Since the File ; System isn't busy, other calls will be processed (and come through here ; as they complete). At some arbitrary time in the future AppleShare will ; finish the transaction with the external volume and will (after kindly ; making sure that the File System isn't busy) jump back to the return ; address that it remembered long ago. This means that we can't save any ; state above the call to external file systems that we'll want after they ; return. ;________________________________________________________________________________ ExtFSHookPatch: proc import QMEnqueue, ExternalMakeFSSpec, GetOffFSQueue import FSDispatchRequest, CallWithRegistersPreserved, Gt1stMatch, GtNxtMatch addq.w #4,sp ; throw away the return address ; Run external file systems if we need to (ExtFSErr, or any error with _MountVol) cmp.w #ExtFSErr,d0 ; Pass request on to external FS's? bne.s @1 ; if not, check for NoMacDskErr <25> cmp.w #$A00F,ioTrap(A0) ; _MountVol could return "ExtFSErr" <25> beq.s @3 ; yes, it is _MountVol <25> bra.s @CallExternals ; No, never mind <25> @1 cmp.w #NoMacDskErr, d0 ; Did we fail a MountVol? bne.s @DoneWithExternals ; if not, move on @3 clr.l ReqstVol ; Force 'No VCB' for this call @CallExternals: move.l FSQHead, a0 ; grab current pb cmp.w #FSQType, ioType(a0) ; better be one of ours bne toDSFSErr ; or end the ballgame move.l d1, d2 ; save possible FCB pointer bsr UnMungeTrapWord ; get trap word (d1) and dispatch or #ExtFSErr (d0) move.w d1, ioTrap(a0) ; set trap word ;; If this an A060 call, then D0.W = selector. After we call ext FS, D0 is trashed, <25> ;; So we need to save it somewhere for FSM foreign file systems. <25> ;; This selector can only be used (by FSM) if old ext FS don't handle the call, <25> ;; because cmdDone is now reentered. <25> movea.l FSVarsPtr, A1 ; A1 = ptr(FSVars block) <25> move.w D0,FSVars.FSSelector(A1); Save trap word or HFS selector for FSM <25> bpl.s @5 ; D0 is HFS selector <25> move.w D1,FSVars.FSSelector(A1); else, save trap word for FSM <25> @5 move.l d2, d1 ; restore possible FCB pointer <25> move.l toExtFS, d2 ; Grab the current external file system chain cmp.l MinusOne, d2 ; Is there anyone on it? beq.s @TryFSM ; No? Go check FSM <25> movea.l d2, a1 ; move vector to jsr'able register ;; The story about TOPS. It takes selector as a long word, <25> ;; so we have to do the following to fix their bug. <25> ext.l d0 ; make it a long <25> jsr (a1) ; call external file system(s) (which remunge the trap word) tst.w d0 ; test the result from the external file systems beq.s @DoneWithExternals ; if error = 0, someone handled the call <25> bpl.s @TryFSM ; if d0 > 0, then no one handled the call, so Try FSM. ; < 0 could be #extFSErr which means nobody handled the call, or a "real" error ; AccessPC returns memFullErr for other FS on a _MountVol call, what should we do? <25> cmp.w #extFSErr,D0 ; is it extFSErr? <25> bne.s @DoneWithExternals ; if not, someone handled the call <25> @TryFSM ; done with the old FnFS, give FSM a chance <25> movea.l FSVarsPtr, A1 ; A1 = ptr(FSVars block) <25> move.l FSVars.FSMHook(A1),D2 ; is FSM there? <25> beq.s @noFSM ; no, all done <25> movea.l D2,A1 ; vector to call <25> move.l FSQHead, a0 ; old ext FS may trashed A0 <26> jsr (A1) ; call FSM external file system(s) <25> tst.w D0 ; any error? <25> beq.s @DoneWithExternals ; if error = 0, someone handled the call <25> cmp.w #extFSErr,D0 ; is it extFSErr? <40> bne.s @DoneWithExternals ; if not, someone handled the call <40> @noFSM bsr UnMungeTrapWord ; get trap word (d1) and dispatch or #ExtFSErr (d0) bsr ExtFSCatchAll ; catch the UnknownCall dispatch that nobody took @DoneWithExternals: move.l FSQHead, a0 ; grab current pb <46> ; Our first patch: MakeFSSpecEmulation ; ; Function ; Check to see if a MakeFSSpec call came back from an external file ; system which didn't implement MakeFSSpec. If so, emulate the call. ; ; Input: ; a0 - pointer to caller's PB ; a1 - pointer to context ; d0 - error code from current file manager call ; ; Output: ; d0 - error code from external file systems (paramErr if they couldn't handle it) ; a0 - pointer to caller's PB ; d2 - trash ; fsCompatQType equ 23 move.w d0, d2 ; save error code in a safe register bsr UnMungeTrapWord ; get trap word (d1) and dispatch or #ExtFSErr (d0) bmi.s @DoneWithEmulation ; MakeFSSpec isn't a straight trap cmp.b #selectMakeFSSpec, d0 ; our selector? bne.s @DoneWithEmulation ; not MakeFSSpec, => cmp.w #paramErr, d2 ; did MakeFSSpec confuse them? beq.s @HandleIt ; yes, so let us help cmp.w #wrgVolTypErr, d2 ; MakeFSSpec on MFS? <29> beq.s @HandleIt ; another job for the compatibility layer <29> cmp.w #extFSErr, d2 ; we fear that some external FS's may reply with ; this error when they don't understand a dispatch beq.s @HandleIt ; so emulate on #extFSErrs, too <14> cmp.w #wPrErr, d2 ; HACK ALERT. iso-9660 file systems are confused <14> bne.s @DoneWithEmulation ; external FS liked it - we're done <14> @HandleIt: jsr GetOffFSQueue ; Get a0 (now FSQHead) off FSQueue moveq.l #fsCompatQType, d2 ; the compatibility layer queue type/refnum bsr QMEnqueue ; get queued up, dispatched to w/CmdDone addr on CL a6 stack jsr ExternalMakeFSSpec ; go do the call and return all the way out move.l (a6)+, -(sp) ; get completion address from a6 stack rts ; jump out of CmdDone, leaving 1 return address on a7 ; Note that the QMgr's completion code kick starts the FS @DoneWithEmulation: ; --- Here's where to add more patches for routines which may have failed or succeeded. SetDSHookInfo: ; <35> ; Check for calls for which we're about to invoke the disk switch hook. ; Save the VCB pointer and the name pointer for the volume we're trying ; to recover. ; ; This solution has a big hole in it. We only do disk recovery for ; synchronous calls, but we can no longer check ioTrap to see if a ; call is synchronous since File Sharing refires sync calls from their ; scheduler. This means that we have to assume that any call which ; has failed with a #volOffLinErr should be the one that the disk switch ; sees. Unfortunately, in between the time we set these variables and the ; time the disk switch code draws the name a truly async call could come ; in for an offline volume and cause the wrong name to be drawn. cmpi.w #volOffLinErr, d2 ; did we get a volume offline error? bne.s @DoneSettingDSHookInfo move.l ReqstVol, a1 pea.l VCBVN(a1) ; grab the name pointer movea.l FSVarsPtr, a1 move.l ReqstVol, fsVars.dsRecoverVCBPtr(a1) ; remember the volume move.l (sp)+, fsVars.dsRecoverNamePtr(a1) ; remember the name of the volume bra CallDone @DoneSettingDSHookInfo IF hasManEject THEN ; ; If the call caused critical volume identification information (name, creation date, and mod date) ; to be changed and the volume is in a manual-eject drive, the information needs to be written to ; disk. ; ; Expected: A2 -> VCB of volume used in call, if any MOVEM.L D0/A0,-(A6) ; save regs MOVE.L A2,D0 ; A2 nil? BEQ.S @DoneFlushCheck ; yes: skip this MOVEA.L VCBQHdr+qHead,A0 ; scan VCB list to make sure A2 is valid @vcbchk MOVE.L A0,D0 ; got one? BEQ.S @DoneFlushCheck ; no: goodbye CMPA.L A0,A2 ; yes: is it the one we're looking for? BEQ.S @chkbit ; yes: check it out MOVEA.L qLink(A0),A0 ; no: move on to the next one BRA.S @vcbchk @chkbit MOVE.W vcbFlags(A0),D0 ; get flags BTST #vcbFlushCritBit,D0 ; need to flush? BEQ.S @DoneFlushCheck ; no: bye MOVEA.L jFlushMDB,A0 ; yes: go flush the volume info JSR (A0) @DoneFlushCheck MOVEM.L (A6)+,D0/A0 ; restore regs ; If _Eject was called for a manual-eject floppy drive, we need to tell the user to remove the disk. ; Expected: A0 -> paramblock ; D0 == dispatch code or ExtFSErr ; D1 == unmunged trap word ; D2 == error code ; Trashes D3-D5. All others preserved. MOVEM.L D0-D2/A0-A4,-(A6) ; save regs CMPI.W #$A017,D1 ; _Eject? BNE @DoneManEject TST.W D2 ; everything went OK? BNE @DoneManEject ; get all the info about the volume we can...we must have the drive number, and hopefully ; we can find a VCB as well MOVEA.L jDtrmV3,A1 ; can we find a VCB from the paramblock? JSR (A1) ; BEQ.S @gotvcb ; found it... SUBA.L A2,A2 ; no VCB--clear the VCB pointer MOVE.W ioVRefNum(A0),D2 ; and get drive number from paramblock BRA.S @getdrv ; find the drive queue element @gotvcb MOVE.W vcbDRefNum(A2),D2 ; after a successful _Eject, the drive number is here @getdrv MOVEA.L DrvQHdr+qHead,A3 ; first drive queue entry @chkdrv MOVE.L A3,D0 ; end of the line? BEQ @DoneManEject ; yep, bail out CMP.W dqDrive(A3),D2 ; is this the one? BEQ.S @gotdrv ; yep, we're set MOVEA.L qLink(A3),A3 ; nope, try the next BRA.S @chkdrv @gotdrv MOVE.W D2,D3 ; drive num in D3 BTST #dqManEjBit,dqInstall(A3) ; is this a manual-eject drive? BEQ @DoneManEject ; none of our business TST.B dqDIP(A3) ; is there a disk in the drive? BGT.S @rmvdisk ; yes: tell the user to remove it BSR @GetManEvt ; no: remove any pending maneject evt for this drive BRA @DoneManEject ; finished @rmvdisk MOVEQ #1,D4 ; set in case we're sharing syserr box with DSHook BTST #7,DSAlertRect ; has DSHook been invoked? BEQ.S @doerr ; yes: alternate alert rect is already set up MOVEQ #0,D4 ; no: note the fact and set up the alternate MOVE.L #$004B006A,DSAlertRect ; alert rect (same as DSHook uses) MOVE.L #$00A50196,DSAlertRect+4 ; now all NEW, all BIGGER DSAlertRect! @doerr MOVEA.L FSVarsPtr,A4 LEA FSVars.dsRecoverNamePtr(A4),A4 MOVE.L (A4),-(A6) ; save the old name SUBA.L #ioQElSize,A6 ; get an ioparamblock on the stack MOVEA.L A6,A0 ; A0 -> iopb SUBA.L #512,A6 ; block buffer on top of stack MOVE.L A2,D0 ; do we have a VCB? BEQ.S @novcb TST.W D4 ; has DSHook been invoked? BEQ.S @usevcb ; no: go ahead and use it CMP.L ReqstVol,A2 ; yes: oops, this is probably DSHook's VCB instead of ours BEQ.S @novcb ; yep, go get the name from the disk @usevcb LEA vcbVN(A2),A0 ; volume name ptr MOVE.L A0,-(A6) ; save it on the stack BRA.S @syserr ; make the alert @novcb ; no vcb, try to read name from disk MOVE.W D3,ioDrvNum(A0) ; fill in paramblock MOVE.W dqRefNum(A3),ioRefNum(A0) MOVE.L A6,ioBuffer(A0) ; get MDB on stack MOVE.W #fsFromStart,ioPosMode(A0) MOVE.L #1024, ioPosOffset(A0) ; MDB is block 2 MOVE.L #512,ioByteCount(A0) ; one block _Read ; read it BNE.S @noname ; failed, just use the drive number CMPI.W #tSigWord,drSigWord(A6) ; is it a TFS vol? BNE.S @noname ; no: just use the drive number LEA drVN(A6),A0 ; volume name ptr MOVE.L A0,-(A6) ; save it on the stack BRA.S @syserr ; make the alert @noname CLR.L -(A6) ; nil name ptr on stack @syserr MOVE.W D3,-(A6) ; drive number on stack MOVE.L A6,(A4) ; dsRecoverNamePtr -> drive num + vol name ptr MOVE.W #dsRemoveDisk,D0 ; syserr ID _SysError ADDA.L #6+512+ioQElSize,A6 ; dispose name info, block buffer, and iopb MOVE.L (A6)+,(A4) ; restore dsRecoverNamePtr BSR.S @WaitManEvt ; wait for the disk to be manually ejected TST.W D4 ; are we inside of DSHook? BNE.S @DoneManEject ; yes: DSHook will clean up the sys alert stuff BSR CleanDSErr ; no: we have to do it, so clean up the alert CLR.W DSErrCode ; and clear the syserr code so we don't freak BRA.S @DoneManEject ; out the Process Mgr (this isn't a real error) @GetManEvt ; see if there's a manual-eject event for the drive MOVEQ #1,D5 ; number given in D3 BRA.S @getevt @WaitManEvt ; await the appearance of a manual-eject event for the MOVEQ #0,D5 ; drive number given in D3 @getevt SUBA.L #EvtBlkSize,A6 ; space for an event record MOVEA.L A6,A0 ; event record @1 MOVE.W #128,D0 ; diskInsertEvt mask _GetOSEvent ; wait for a disk-insert event BNE.S @chkexit ; didn't get one--keep waiting? CMPI.W #-2,evtMessage(A0) ; msg hi = -2 for manual eject BNE.S @repost ; put this one back and try again MOVE.W evtMessage+2(A0),D1 ; msg lo = drive number CMP.W D1,D3 ; is it our drive? BEQ.S @evtexit ; yes: exit ; no: put it back @repost MOVE.L evtMessage(A0),D0 ; get the message MOVEA.W #DiskInsertEvt,A0 ; and the type _PostEvent ; put it back on the end of the queue @chkexit TST.W D5 ; should we keep waiting for an event? BEQ.S @1 ; yes, go check again @evtexit ADDA.L #EvtBlkSize,A6 ; clean up RTS ; goodbye @DoneManEject MOVEM.L (A6)+,D0-D2/A0-A4 ; restore regs ENDIF ; All of the patches beneath this label are for calls that worked <20> tst.w d2 ; did the call work? <20> bne CallDone ; if not, go try another call <20> ; patch: UnTagFCBs ; Registers now: ; a0 - caller's PB ; d0 - dispatch or #ExtFSErr ; d1 - unmunged trap word ; d2 - error code ; On all _Close calls, clear the process serial number stored in the ; parallel FCB array. and.w #$f0ff, d1 ; clear modifier bits from trap word cmp.w #$a001, d1 ; was this _Close? bne.s @CloseTagDone ; if not, we're done here @CloseAttempted: move.w ioRefNum(a0),d0 ; get refNum bsr ParallelFCBFromRefnum ; a1 = address of parallel FCB element clr.l xFCBPid1(a1) ; clear high long of PSN clr.l xFCBPid2(a1) ; clear low long of PSN <20> bra CallDone ; go do another call, if necessary @CloseTagDone: @UnTagWDCBs: ; do WDCBs the same way we do FCBs <20> cmpi.w #selectCloseWD, d0 ; is this a _CloseWD call? bne.s @UnTagWDCBsDone move.w ioVRefNum(a0), d0 ; get WDRefNum cmp.w #WDRfnMax,d0 ; Is it a VRefNum? <23> bhi.s @UnTagWDCBsDone ; vRefNum (i.e. WD's to the root) don't get tagged <23> bsr ParallelWDCBFromRefnum ; a1 = address of parallel WDCB element clr.l xWDCBPid1(a1) clr.l xWDCBPid2(a1) bra CallDone @UnTagWDCBsDone: FixFCBSyncOnOpenRF: ; Fix FCB synchronization on _OpenRF ; Registers now: ; a0.l - pb ; d0 - dispatch or #ExtFSErr ; d1.w - trap word w/o modifiers ; d2.w - result code ; ; We only need to synchronize on successful local _OpenRF calls. ; External file systems are responsible for keeping their own FCB marbles in order. ; (for example, some may not use a unique file number for each fcb) ; Note that we don't patch _OpenRFDeny because it's not a local FS call. cmp.w #$a00a, d1 ; was this _OpenRF? bne.s @DoneCheckingOpenRF ; if not, we're out of here ; Make sure this successful _OpenRF was local, and if so fix up FCBs of any other open paths. @FixOpenRFRegs reg d2/d4 ; save regs (no i/o 'till we restore these) <20> movem.l @FixOpenRFRegs, -(sp) ; save working registers move.l FCBsPtr,a1 ; a1 = fcb array move.w ioRefNum(a0),d4 ; d4 = refnum of newly opened RF movea.l fcbVptr(a1,d4.w),a2 ; a2 = VCB tst.w vcbFSID(a2) ; are we local? <20> bne.s @NoOtherPaths ; if not, it's not our problem move.l fcbFlNm(a1,d4),d2 ; d2 = file num jsr Gt1stMatch ; get an FCB which matches VCB & file num ; Now A1 --> FCBs, d1 = refnum of the matching FCB @FixOpenRFLoop cmp.w d1,d4 ; is it the one that we just got opened? beq.s @SawOurselves ; if so, ignore it move.b FCBMdRByt(a1,d1),d3 ; mod byte btst #fcbRscBit,d3 ; resource fork? bne.s @DoAdjust ; Adjust EOF if the forks match @SawOurselves: jsr GtNxtMatch ; look for another matching fcb beq.s @FixOpenRFLoop ; br if match bra.s @NoOtherPaths ; don't adjust if we're the only path @DoAdjust: bsr adjEOF ; Make our new FCB match old wise one move.b d3,FCBMdRByt(a1,d1) ; don't let AdjEOF change dirty bit @NoOtherPaths: movem.l (sp)+,@FixOpenRFRegs ; error code d0 and param a0 unchanged @DoneCheckingOpenRF: ; --- Here's where to add more patches for routines which succeed if 0 then ; you might want this if you add patches bra.s CallDone endif ; The current call is now done. CallDone: ; Check to see if thereΥs another call to execute. move sr, -(sp) ; save interrupt state <27Aug85> ori #HiIntMask, sr ; only debug interrupts allowed clr.w FSBusy ; we're not busy anymore ; delete the current request from the queue and post any completion routines move.l FSQHead,a0 ; a0 -> current request cmp.w #FSQType,IOType(a0) ; it better be an I/O queue element bne toDSFSErr ; or else die move.l qLink(a0),FSQHead ; get next request, if any bne.s @CallCompletion ; branch if queue not empty <27Aug85> clr.l FSQTail ; clear tail ptr, too @CallCompletion: ; <27Aug85> move (sp)+,sr ; restore interrupt state move.w d2,IOResult(a0) ; post error code move.w d2,d0 ; put result code into d0 <21> move.l ioCompletion(a0),d1 ; is there a completion routine? beq.s @DispatchNext ; skip if there's not <27Aug85> move.l d1,a1 ; get the completion routine address jsr (a1) ; call completion routine @DispatchNext: move sr,-(sp) ; save interrupt state ori #HiIntMask,sr ; only debug interrupts allowed tst.l FSQHead ; is a command pending? beq.s @Done bset.b #fsBusyBit, FSBusy ; make sure the file system is marked busy bne.s @Done ; if it was already busy, we can return asynchronously move.w (sp)+,sr ; restore interrupt state lea.l FSDispatchRequest,a0 ; our dispatcherΥs entry point <48> jmp CallWithRegistersPreserved ; call it <48> @Done: move.w (sp)+,sr ; restore interrupt state and return rts toDSFSErr: moveq.l #DSFSErr,d0 _SysError ; AdjEOF -- Call through the jAdjEOF vector AdjEOF: move.l jAdjEOF,-(sp) ; jumptable entry for vAdjEOF rts ; go there endproc