mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-25 09:30:50 +00:00
5417 lines
220 KiB
Plaintext
5417 lines
220 KiB
Plaintext
;
|
||
; File: FileMgrPatches.a
|
||
;
|
||
; Contains: Patches to the file system.
|
||
;
|
||
; Copyright: © 1990-1992 by Apple Computer, Inc., all rights reserved.
|
||
;
|
||
; Change History (most recent first):
|
||
;
|
||
; <SM1> 4/15/92 kc Removed the "ROM" prefix from the RomBind routines.
|
||
; Added comments to all of the patches that were rolled in.
|
||
; • Pre-SuperMario comments follow •
|
||
; <56> 3/18/92 gs rolled in Disappearing Folder Fix from Presto (patch to CMSetup)
|
||
; Pete said it was Okay. No, Really, he did!
|
||
; <55> 11/25/91 DTY Make a new call, GetParallelFCBFromRefNum, and give it selector
|
||
; $50. This eventually calls ParallelFCBFromRefNum, but does some
|
||
; sanity checking beforehand.
|
||
; <54> 8/30/91 DTY Define has3rdFloppy here since it’s no longer defined in
|
||
; BBSStartup. Defined to be 0 because has3rdFloppy was 0 for
|
||
; System builds before.
|
||
; <53> 6/25/91 BH fix ClosePatch which was incorrectly pushing kSystemProcess onto
|
||
; the stack which resulted in the check to see if a file was
|
||
; opened by kSystemProcess to always fail (changed code to use PEA
|
||
; kSystemProcess instead of PEA #kSystemProcess)
|
||
; <52> 4/4/91 dnf csd, #86223: Have Basic I/O pass VM errors from the driver
|
||
; through to the caller. Currently notEnoughMemoryErr is the only
|
||
; VM error we expect.
|
||
; <51> 3/28/91 dnf ewa, #CheckDesktopSupportFails: The GetVolParms call returns the
|
||
; wrong value for bHasDesktopMgr for small non-ejectable volumes.
|
||
; <50> 3/27/91 KST bb, ksTung#16: Fixing a bug in ROM BTDelete which used MOVE.W to
|
||
; move a long word.
|
||
; <49> 3/19/91 dnf dba, dty: Make sure the lenient remount check in ROM doesn’t
|
||
; trigger.
|
||
; <48> 3/17/91 dnf dba, dty, #84670, #82944, #84977 : use
|
||
; CallWithRegistersPreserved in dispatching; save D0 across calls
|
||
; to completion routines. Save trashed registers across btree
|
||
; calls on the Mac Plus.
|
||
; <47> 3/11/91 dnf dba, #dnf10: nuke MakePBLookAsync since it never did any good
|
||
; <46> 3/11/91 dnf dba, #83239: stop relying on external file systems to preserve
|
||
; A0; for example, AppleShare client trashes A0 on a _GetVolParms
|
||
; <45> 3/11/91 dnf dba, #84288: flush the cache properly after writing the trap
|
||
; word in TrapAgain in low memory, but before jumping there; this
|
||
; is especially important for 68040 processors; we had to fix this
|
||
; in the code that re-fires a trap after running out of FCBs, but
|
||
; we didn’t have to fix it for the Disk Switch Hook, since it
|
||
; calls MountVol, which calls BlockMove, which calls CacheFlush
|
||
; <44> 3/11/91 dnf dba, #84430,#83779: fix the extents truncation patch so that it
|
||
; is no longer confused between data and resource forks; this
|
||
; fixes fragmentation that was occurring when the Finder did
|
||
; copies
|
||
; <43> 3/11/91 dnf dba, #84257: Fix the DesktopCloseDownProc so that it works even
|
||
; when the Unmount is done by name. This was fixed by copying the
|
||
; string passed to Unmount onto the stack before calling
|
||
; GetVolInfo.
|
||
; <42> 2/28/91 dnf dba, #84095: Fix the DesktopCloseDownProc to work when called
|
||
; with parameters that aren't vRefNums.
|
||
; <41> 2/26/91 dnf gbm,bbm, #83780: Enlarge the file system stack.
|
||
; <40> 2/26/91 KST dnf, ksTung#15: FSM Hook should not call "UnMungTrapWord" if FSM
|
||
; defined foreign file system returns a real error.
|
||
; <39> 2/26/91 csd dba: Add the export for FileManagerIdleProc, fix the patches to
|
||
; FileOpen and OpenRF so that they set the RegRsrc global.
|
||
; <38> 2/25/91 dnf dfh, #80720,#82672: Fix the FCBs problem by implementing a new
|
||
; file system call, _AllocateFCBs.
|
||
; <37> 2/15/91 KST dnf, #83093: CMRenameCN binds to the wrong ROM address on the
|
||
; Plus.
|
||
; <36> 2/12/91 dnf gbm, #81716: Move the call to DTCloseDown from the Desktop
|
||
; Manager's unmount notification hook to a patch to _Unmount.
|
||
; This will ensure that it gets called after we know that none of
|
||
; the parties on the Process Manager hook have decided to reject
|
||
; the unmount.
|
||
; <35> 2/7/91 dnf kst, #79676: Set dsRecoverVCBPtr inside of CmdDone on all
|
||
; #offlineErrs. This eliminates (most of) the reentrancy problems
|
||
; with the name drawn by the disk switch hook.
|
||
; <34> 1/25/91 dnf dba, #bo3b1: Make the file system not defer itself on sync
|
||
; calls. This allows macsbug to use sync calls to implement its
|
||
; log and reboot features.
|
||
; <33> 1/21/91 dnf (ewa) Clear the error code for write-protected remounts.
|
||
; <32> 1/20/91 dnf (Eric Anderson) _Eject and _Offline need to set the “VCB
|
||
; unmounted OK” bit with a read-modify-write to the MDB.
|
||
; <31> 1/18/91 dnf (kst) Fix ROM address for FSQueueSync on the SE.
|
||
; <30> 1/18/91 dnf (dba) Use a ROMBind for FindDrive since it isn't vectored on the
|
||
; Plus and SE. Change _Unmount to make direct calls to FClose and
|
||
; TrashVBlocks instead of calling FlushBuffers, which attempts to
|
||
; set the “VCB unmounted OK” bit with a read-modify-write to the
|
||
; MDB. Patch out most of _Offline & _Eject since <29> needs the
|
||
; jFClose vector which doesn't exist on the Plus and SE.
|
||
; <29> 1/16/91 dnf (csd) Change _Offline and _Eject to never close the volume btree
|
||
; files and _UnMount to always close them.
|
||
; <28> 1/15/91 KST With dnf: ExtFSCatchAll should return if D0 is negative or zero.
|
||
; <27> 1/14/91 KST With BB: patch to CMCreateCN and CMUpdateCN to mark VCB dirty if
|
||
; VCBModTime has been changed. The other half of the patch is in
|
||
; 'CMSvcsExtra.a'.
|
||
; <26> 1/7/91 KST With dnf: Set up A0=ioParam before calling FSM hook.
|
||
; <25> 1/4/91 KST With dnf: Adding FSM hook in.
|
||
; <24> 12/26/90 dnf (csd) Make the disk switch hook think that it should never save
|
||
; bits behind the disk switch alert. The file system now relies
|
||
; completely on the system error handler and update events for
|
||
; drawing the alert and cleaning up afterwards.
|
||
; <23> 12/21/90 dnf (dfh) Have UnTagWDCBs not attempt to untag a vRefNum that is
|
||
; serving as a WD to the root.
|
||
; <22> 12/18/90 dnf (dba) Fix the second case in the Rename patch
|
||
; <21> 12/18/90 dnf (dba) Return correct error codes from async. calls (change to
|
||
; CmdDone).
|
||
; <20> 12/16/90 dnf (dba) Patch the disk switch hook to fail with a #volOffLinErr on
|
||
; #memFull from _MountVol. Patch _MountVol to not attempt to get
|
||
; more memory by calling _Offline on other already mounted
|
||
; volumes. Changed tst.l on FSID to tst.w in OpenRF sychronization
|
||
; patch. Saved fewer registers here also. Fixed both _SetVolInfo
|
||
; and _Rename to always do the right thing when renaming volumes.
|
||
; (nil, null, > 27, and keeping the root in sync with the vcb).
|
||
; <19> 12/16/90 dnf (dfh) Do a real UnTagWDCBs
|
||
; <18> 12/14/90 dnf (djw) Add SCSI-is-busy deferral code. Replace FSQueue and CmdDone
|
||
; logic by grabbing control at FSQueueHook and ExtFSHook.
|
||
; <17> 12/13/90 KST With Bill B.: Adding a come from patch to TruncateFile to fix a
|
||
; bug in FClose. See detail in the code.
|
||
; <16> 12/6/90 dnf (dfh) Get rid of obsolete ROM jump table source that didn’t
|
||
; generate any code anyway. Implement _Close patch so that file
|
||
; that were opened in System Mode cannot be closed outside of
|
||
; system mode.
|
||
; <15> 9/26/90 dnf (dnf/pwd) Use the ol' TrapAgain lomem to re-fire calls that have been
|
||
; rescued by the more fcbs/more wdcbs code. (dnf/th) Change SetFilLock and
|
||
; RstFilLock to use the new definition of FXInfo.
|
||
; <14> 9/25/90 dnf (dnf/sab) Make sure that the dispatcher preserves the trap word
|
||
; in d1. Emulate MakeFSSpec on wPrErr for confused iso-9660 disks.
|
||
; <13> 9/23/90 csd Changed fsDeferProc to use the right register instead of jumping
|
||
; to random code which does random things with Darin’s name.
|
||
; <12> 9/22/90 dnf Clear the fsDeferFlag when the deferred task fires.
|
||
; <11> 9/22/90 csd (dfh) Fixed a bug in ParallelFCBFromRefnum which implicitly
|
||
; expected the high word of D0 to be clear. Fixed the
|
||
; fixDiskSwitch patch to not destroy command-. events.
|
||
; <10> 9/22/90 dnf oops... we can't use D1 to put the trap word in...
|
||
; <9> 9/22/90 dnf (dnf/pwd/dfh) Modified a060 patch to dispatch all possible
|
||
; (that's 255) hfs dispatch selectors. This obviates the need for
|
||
; any patches like ASFSInit which add new selectors by stealing
|
||
; calls off of a060. Added MakeFSSpec emulation Add fsQueueHook
|
||
; patch (FCB & WDCB expansion, process serial number recording,
|
||
; file system interrupt level deferring) Change ExtFSPatch (move
|
||
; most stuff to fsQueueHook.) Add toExtFS patch (catch
|
||
; unimplemented a060 selectors)
|
||
; <8> 8/29/90 dnf Change selectGetAltAccess to selectGetForeignPrivs
|
||
; <7> 8/28/90 dnf Change references to selectCreateFileID to
|
||
; selectCreateFileIDRef.
|
||
; <6> 8/15/90 KST Grow the WDCB (and FCB) array if we are running out of WDCB.
|
||
; <5> 8/10/90 KST Renaming NuLenBTCB to lenBTCB70.
|
||
; <4> 8/6/90 dnf Rename __MakeFSSpec to MakeFSSpec
|
||
; <3> 8/6/90 KST Fixing a bug in OpenWD patch. If MultiFinder is not ready, we
|
||
; need to compare appl's ProcID.
|
||
; <2> 7/30/90 dnf Move all of the 7.0 patches into here; convert them to linked
|
||
; patch format
|
||
; <1> 4/20/90 dba first checked in
|
||
;
|
||
;
|
||
;
|
||
; Change History from HFS70PatchTail.a
|
||
;
|
||
; <23+> 7/24/90 dnf Convert to linked patch
|
||
; <23> 7/2/90 dnf Add afp inhibit bits to Set/RstFlock calls + referenced ROM
|
||
; addresses
|
||
; <22> 6/20/90 KST Don't grow FCB array beyond limit (16 bits positive word). The
|
||
; max. FCB is now 344. Also fixed the bug that doesn't close every
|
||
; 8th file after 40.
|
||
; <21> 6/1/90 dnf Add rom addresses for CMGetCN. Change _OpenDF to set the trap
|
||
; word to _Open (Ax00) for the benefit of existing external file
|
||
; systems.
|
||
; <20> 5/4/90 dnf Add dispatch to MakeFSSpec
|
||
; <19> 4/17/90 KST Fixing a bug that calls "adjEOF" with the wrong Refnum.
|
||
; <18> 4/10/90 KST Fixing a bug in FOpen, it doesn't do "adjEOF" if openning a
|
||
; resource file.
|
||
; <17> 4/10/90 KST Need to restore ioParam in A0 after calling external FS.
|
||
; <16> 4/9/90 dnf Add DTDelete to this patch's dispatch table; change all call
|
||
; names to match final versions.
|
||
; <15> 3/28/90 KST Clear D4 before storing FCB and divide by FCBLen.
|
||
; <14> 3/20/90 KST fixed a branch error.
|
||
; <13> 3/20/90 KST Record PSN or grow the FCB/WDCB only for SYNC open calls
|
||
; <12> 3/20/90 KST Record PSN or grow the FCB only for SYNC open calls.
|
||
; <11> 3/20/90 dnf Don't attempt to send shared environment calls out the
|
||
; toExtFSHook.
|
||
; <10> 3/20/90 KST Enabling the recording of PSN (again). Added support for AFP;
|
||
; pop off return address for ASYNC call; saved D1 for AppleShare.
|
||
; <9> 3/16/90 dnf Add stubs for volume and refnum based calls
|
||
; <8> 2/21/90 KST 1. Enable the recording of PSN. 2. Don't close system files on
|
||
; FSCleanUp (fileNum < 16).
|
||
; <7> 2/15/90 DNF Don't step on the result code out of GetVolParms
|
||
; <6> 2/10/90 csd with dnf. Disabled the tail patch to ExtFSHook which watched for
|
||
; the opening of files and recorded them in a parallel structure.
|
||
; The scheme prevented the unmounting of servers.
|
||
; <5> 2/6/90 KST Fixing a bug in OpenRF that doesn't record process serial number
|
||
; when openning RFile.
|
||
; <4> 2/4/90 dnf Changed equates to use StandardEqu.d
|
||
; <3> 1/23/90 csd with dnf: replaced use of constant openWDN with fsOpenWD which
|
||
; is more appropriate.
|
||
; <2> 1/9/90 dnf Move some private equates directly into this file Add CheckCSPB,
|
||
; OpenDF and xOpenDT
|
||
; <3.3> 12/8/89 dnf Changed GetVolParms' buffer
|
||
; <3.2> 11/21/89 KST Fixed a bug when growing parallel array. Forgot to include
|
||
; header length.
|
||
; <3.1> 11/17/89 dnf Moved dispatch #'s from 40-4f to 60.
|
||
; <3.0> 10/30/89 KST Fixed some ROM addresses. It should use jump table address.
|
||
; <2.9> 10/30/89 dnf AlkBlkSiz is a word, even though it is stored as a long
|
||
; <2.8> 10/17/89 KST Fixed ROM addresses for Esprit.
|
||
; <2.7> 10/16/89 KST Added routines for FSCleanUp HFS call. Allocate FCB and WDCB
|
||
; parallel array at boot time. Patched OpenWD and FileOpen to
|
||
; store Process serial ID in the parallel array. Grow FCB/WDCB
|
||
; array and friends dynamically.
|
||
; <2.6> 10/11/89 dnf updated GetVolParms, added AltPrivsStub, removed XGetInfo call
|
||
; <2.5> 9/29/89 EKN Added Gt1stFcb, GtNxtFcb, FlushCache
|
||
; <2.4> 9/18/89 dnf Added XGetInfo call
|
||
; <2.3> 9/7/89 dnf dnf for kst. Roll in BTree writecount patch
|
||
; <2.2> 8/26/89 dnf Add Aurora (Mac IIci) and Esprit (Mac Portable) ROM addresses
|
||
; <2.1> 8/7/89 dnf Don't clobber d0 in dispatch, get rid of PRNonPortable
|
||
; <2.0> 8/7/89 EMT Use inc.sum.d rather than individual includes for speed and
|
||
; accuracy.
|
||
; <1.9> 7/24/89 EKN Added TFSVCBTst, Gt1stMatch. Changed FileIDs call names from
|
||
; "PB.." to "PBH...".
|
||
; <1.8> 7/6/89 dnf Enable desktop db mgr. Change references to ExtOffLinCh to
|
||
; ExtOffLinCk to match ROM
|
||
; <1.7> 6/1/89 EKN Get PushCName and PopCName to different rom addresses, please.
|
||
; <1.6> 5/31/89 dnf Really disable Desktop Mgr this time.
|
||
; <1.5> 5/31/89 dnf Desktop mgr hangs in btree mgr. Disable Desktop mgr for 7.0a1 so
|
||
; machine won't hang opening Desktop DB
|
||
; <1.4> 5/31/89 dnf Back out of fancy new DCBind macro to old (reliable) JmpBind
|
||
; <1.3> 5/31/89 dnf Reordered equates. HFS70Equ needs to be before CatSrchPriv.a
|
||
; <1.2> 5/31/89 EKN Fixed Rom address for BTSearch. Added entries for CMDelete,
|
||
; CMRename, CMMove
|
||
; <1.1> 5/31/89 EKN Filled in rom addresses, hooked in CMSetUp correctly,and unset
|
||
; IsAnINIT.
|
||
; <1.0> 5/30/89 dnf Integrate CatSearch, FileID's and Desktop Database Mgr into one
|
||
; ptch
|
||
;
|
||
; To Do:
|
||
;
|
||
;
|
||
;——————————————————————————————————————————————————————————————————————————————————————————
|
||
; Includes…
|
||
;——————————————————————————————————————————————————————————————————————————————————————————
|
||
|
||
;
|
||
; <54> has3rdFloppy is false for System builds.
|
||
;
|
||
|
||
if (&type('has3rdFloppy') = 'UNDEFINED') then ; <54>
|
||
has3rdFloppy: equ 0 ; <54>
|
||
endif ; <54>
|
||
|
||
|
||
print push
|
||
print off
|
||
load 'StandardEqu.d'
|
||
include 'FileMgrPrivate.a'
|
||
include 'CatSrchPriv.a'
|
||
include 'BTreeEqu.a'
|
||
include 'DTDBMgrPriv.a'
|
||
include 'DTDBMgrEqu.a'
|
||
include 'LinkedPatchMacros.a'
|
||
include 'QMgrEqu.a'
|
||
include 'GestaltEqu.a'
|
||
include 'MFPrivate.a'
|
||
include 'Processes.a'
|
||
include 'HardwarePrivateEqu.a'
|
||
include 'SysPrivateEqu.a'
|
||
include 'SCSIEqu.a'
|
||
include 'SCSIPriv.a'
|
||
include 'SonyEqu.a'
|
||
include 'InternalMacros.a'
|
||
print pop
|
||
|
||
|
||
BuildHFS70Init default 0
|
||
|
||
;——————————————————————————————————————————————————————————————————————————————————————————
|
||
; exciting table of ROM Addresses
|
||
;——————————————————————————————————————————————————————————————————————————————————————————
|
||
ROMs Plus,SE,II,Portable,IIci
|
||
|
||
romAfterExtFSHook ROMBind (Plus,$029f2), (SE,$04434), (II,$07edc), (IIci,$0f0e0), (Portable, $096e0)
|
||
romBTFlush ROMBind (Plus,$068c2), (SE,$08398), (II,$0be5c), (IIci,$13242), (Portable, $0d802)
|
||
BUILDKEY ROMBind (Plus,$065d8), (SE,$080a0), (II,$0BB64), (IIci,$12f40), (Portable, $0d50a)
|
||
BTDELETE ROMBind (Plus,$0671c), (SE,$081e6), (II,$0bcaa), (IIci,$13090), (Portable, $0d650)
|
||
BTGETRECORD ROMBind (Plus,$06928), (SE,$083fc), (II,$0bec0), (IIci,$132A6), (Portable, $0d866)
|
||
BTINSERT ROMBind (Plus,$069ea), (SE,$084b4), (II,$0bf78), (IIci,$1335E), (Portable, $0d91e)
|
||
BTSEARCH ROMBind (Plus,$06d4a), (SE,$0880c), (II,$0c2d0), (IIci,$136B6), (Portable, $0dc76)
|
||
BTUPDATE ROMBind (Plus,$06dd6), (SE,$08896), (II,$0c35a), (IIci,$13740), (Portable, $0dd00)
|
||
CACHERDIP ROMBind (Plus,$07b2a), (SE,$0967e), (II,$0d13e), (IIci,$1459C), (Portable, $0eb44)
|
||
romChgMFlLock ROMBind (Plus,$046de), (SE,$06812), (II,$0a2e8), (IIci,$11694), (Portable, $0bc7c)
|
||
CHKNODE ROMBind (Plus,$072c2), (SE,$08e22), (II,$0c8e6), (IIci,$13D42), (Portable, $0e2ec)
|
||
romCkExtFS ROMBind (Plus,$03638), (SE,$050be), (II,$08b80), (IIci,$0fefa), (Portable, $0a4e2)
|
||
romCkFilMod ROMBind (Plus,$0497a), (SE,$06420), (II,$09eec), (IIci,$11298), (Portable, $0b880)
|
||
CMDDONE ROMBind (Plus,$0295e), (SE,$043a0), (II,$07e48), (IIci,$0f04c), (Portable, $0964c)
|
||
CMFLUSH ROMBind (Plus,$06606), (SE,$080ce), (II,$0BB92), (IIci,$12f6e), (Portable, $0d538)
|
||
CMGETCN ROMBind (Plus,$06238), (SE,$07d04), (II,$0b7c8), (IIci,$12ba2), (Portable, $0d16e)
|
||
CMSETUP ROMBind (Plus,$065a0), (SE,$08068), (II,$0bb2c), (IIci,$12f06), (Portable, $0d4d2)
|
||
romCMUpdateCN ROMBind (Plus,$0656c), (SE,$08036), (II,$0bafa), (IIci,$12ed4), (Portable, $0d4a0)
|
||
CVFLGS ROMBind (Plus,$03a76), (SE,$05502), (II,$08FCA), (IIci,$10344), (Portable, $0a92c)
|
||
romDtrmV1 ROMBind (Plus,$03650), (SE,$050e2), (II,$08ba4), (IIci,$0ff1e), (Portable, $0a50c)
|
||
DTRMV3 ROMBind (Plus,$03642), (SE,$050ce), (II,$08b90), (IIci,$0ff0a), (Portable, $0a4f8)
|
||
EXTOFFLINCK ROMBind (Plus,$04aba), (SE,$06560), (II,$0A02C), (IIci,$113d8), (Portable, $0b9c0)
|
||
romFindDrive ROMBind (Plus,$03414), (SE,$04e9A), (II,$08956), (IIci,$0FC6C), (Portable, $0A258)
|
||
FNDFILNAME ROMBind (Plus,$04990), (SE,$06436), (II,$09f02), (IIci,$112ae), (Portable, $0b896)
|
||
romFSAsync ROMBind (Plus,$02912), (SE,$04354), (II,$07dfc), (IIci,$0f000), (Portable, $09600)
|
||
FSQUEUESYNC ROMBind (Plus,$028a6), (SE,$042e8), (II,$07d90), (IIci,$0ef94), (Portable, $09594)
|
||
FSQUEUE ROMBind (Plus,$028aa), (SE,$042ec), (II,$07d94), (IIci,$0ef98), (Portable, $09598)
|
||
romGt1stMatch ROMBind (Plus,$04002), (SE,$05a9c), (II,$0955c), (IIci,$108d6), (Portable, $0aebe)
|
||
romLocCNode ROMBind (Plus,$06678), (SE,$0813e), (II,$0BC02), (IIci,$12FDE), (Portable, $0d5a8)
|
||
LOCCREC ROMBind (Plus,$06698), (SE,$0815e), (II,$0bc22), (IIci,$12FFE), (Portable, $0d5c8)
|
||
LOCREC ROMBind (Plus,$07538), (SE,$0908c), (II,$0cb50), (IIci,$13FAC), (Portable, $0e556)
|
||
romMarkVCB ROMBind (Plus,$02d1e), (SE,$04766), (II,$0820e), (IIci,$0F524), (Portable, $09b10)
|
||
romMarkVCBTime ROMBind (Plus,$02d20), (SE,$04768), (II,$08210), (IIci,$0F526), (Portable, $09b12)
|
||
POPCNAME ROMBind (Plus,$04288), (SE,$05d28), (II,$097EA), (IIci,$10b96), (Portable, $0b17e)
|
||
PUSHCNAME ROMBind (Plus,$04258), (SE,$05cf8), (II,$097BA), (IIci,$10b66), (Portable, $0b14e)
|
||
romRfnCall ROMBind (Plus,$05352), (SE,$06e04), (II,$0a8c8), (IIci,$11C86), (Portable, $0c26e)
|
||
TFSVCBTST ROMBind (Plus,$04ec6), (SE,$06968), (II,$0a43e), (IIci,$117ea), (Portable, $0bdd2)
|
||
UPDCNAME ROMBind (Plus,$066c2), (SE,$0818c), (II,$0BC50), (IIci,$1302C), (Portable, $0d5f6)
|
||
romUpdRtCnts ROMBind (Plus,$065be), (SE,$08086), (II,$0BB4A), (IIci,$12f24), (Portable, $0d4f0)
|
||
romUpdVCnts ROMBind (Plus,$065ae), (SE,$08076), (II,$0BB3A), (IIci,$12f14), (Portable, $0d4e0)
|
||
XFFLUSH ROMBind (Plus,$05ed0), (SE,$0799a), (II,$0B45E), (IIci,$1282a), (Portable, $0ce04)
|
||
GT1STFCB ROMBind (Plus,$03ff2), (SE,$05a8c), (II,$0954c), (IIci,$108c6), (Portable, $0aeae)
|
||
GTNXTFCB ROMBind (Plus,$03ffa), (SE,$05a94), (II,$09554), (IIci,$108ce), (Portable, $0aeb6)
|
||
FLUSHCACHE ROMBind (Plus,$076c6), (SE,$0921a), (II,$0ccdc), (IIci,$1413a), (Portable, $0e6e2)
|
||
|
||
; The following addresses are needed for ROM B*Tree writecount patch: <2.3 kst>
|
||
romBuildIRec ROMBind (Plus,$072a0), (SE,$08E00), (II,$0C8C4), (IIci,$13D20), (Portable,$0E2CA)
|
||
romClrNode ROMBind (Plus,$0734E), (SE,$08EA6), (II,$0C96A), (IIci,$13DC6), (Portable,$0E370)
|
||
romDeleteRec ROMBind (Plus,$0736A), (SE,$08EC2), (II,$0C986), (IIci,$13DE2), (Portable,$0E38C)
|
||
romGetLtSib ROMBind (Plus,$073BA), (SE,$08F12), (II,$0C9D6), (IIci,$13E32), (Portable,$0E3DC)
|
||
romGetRtSib ROMBind (Plus,$073C4), (SE,$08F1C), (II,$0C9E0), (IIci,$13E3C), (Portable,$0E3E6)
|
||
romGetMaxKey ROMBind (Plus,$073F6), (SE,$08F4C), (II,$0CA10), (IIci,$13E6C), (Portable,$0E416)
|
||
romGetRecA ROMBind (Plus,$0746E), (SE,$08FC2), (II,$0CA86), (IIci,$13EE2), (Portable,$0E48C)
|
||
romInitNode ROMBind (Plus,$0748A), (SE,$08FDE), (II,$0CAA2), (IIci,$13EFE), (Portable,$0E4A8)
|
||
romInsertRec ROMBind (Plus,$074BC), (SE,$09010), (II,$0CAD4), (IIci,$13F30), (Portable,$0E4DA)
|
||
romLocBTCB ROMBind (Plus,$0752E), (SE,$09082), (II,$0CB46), (IIci,$13FA2), (Portable,$0E54C)
|
||
romLocTPR ROMBind (Plus,$0756A), (SE,$090BE), (II,$0CB82), (IIci,$13FDE), (Portable,$0E588)
|
||
romUpdDRec ROMBind (Plus,$07646), (SE,$0919A), (II,$0CC5E), (IIci,$140BA), (Portable,$0E664)
|
||
romUpdIKey ROMBind (Plus,$07668), (SE,$091BC), (II,$0CC80), (IIci,$140DC), (Portable,$0E686)
|
||
romBTCleanUp ROMBind (Plus,$06E02), (SE,$088C2), (II,$0C386), (IIci,$1376C), (Portable,$0DD2C)
|
||
romBTSetUp ROMBind (Plus,$06E10), (SE,$088D0), (II,$0C394), (IIci,$1377A), (Portable,$0DD3A)
|
||
romRotateLt ROMBind (Plus,$0706C), (SE,$08BC8), (II,$0C68C), (IIci,$13AE0), (Portable,$0E092)
|
||
romSplitLt ROMBind (Plus,$0715C), (SE,$08CB8), (II,$0C77C), (IIci,$13BD0), (Portable,$0E182)
|
||
romTreeSearch ROMBind (Plus,$071E4), (SE,$08D42), (II,$0C806), (IIci,$13C5A), (Portable,$0E20C)
|
||
|
||
; The following addresses are needed for file/WD cleanup patch: <2.7 kst>
|
||
romFillWDCB ROMBind (Plus,$03BB4), (SE,$05640), (II,$09100), (IIci,$1047A), (Portable,$0AA62)
|
||
romGtNxtMatch ROMBind (Plus,$04010), (SE,$05AAA), (II,$0956A), (IIci,$108E4), (Portable,$0AECC)
|
||
;PermssnChk ROMBind (Plus,$0416C), (SE,$05C06), (II,$096C8), (IIci,$10A74), (Portable,$0B05C)
|
||
;MFSOpen ROMBind (Plus,$04294), (SE,$05D34), (II,$097F6), (IIci,$10BA2), (Portable,$0B18A)
|
||
romGt1stWDCB ROMBind (Plus,$043A0), (SE,$05E3A), (II,$098FC), (IIci,$10CA8), (Portable,$0B290)
|
||
romGtNxtWDCB ROMBind (Plus,$043A8), (SE,$05E42), (II,$09904), (IIci,$10CB0), (Portable,$0B298)
|
||
|
||
FromWaitForDsk ROMBind (Plus,$2B66),(SE,$45A8),(II,$8050)
|
||
AfterSysErrorInDSHook ROMBind (Plus,$2B30),(SE,$4572),(II,$801A),(Portable,$9830),(IIci,$F244)
|
||
DSExit ROMBind (Plus,$2BE8),(SE,$4630),(II,$80D8),(Portable,$98EC),(IIci,$F300)
|
||
AfterMountVolInDSHook ROMBind (Plus,$2AAE),(SE,$44F0),(II,$7F98),(Portable,$97A8),(IIci,$F1A8)
|
||
|
||
OldMtVolAfterFSQueue ROMBind (Plus,$2E4C),(SE,$489E),(II,$834C),(Portable,$9C4E),(IIci,$F662)
|
||
MtVolOK ROMBind (Plus,$3138),(SE,$4B86),(II,$863A),(Portable,$9F3C),(IIci,$F950)
|
||
MtCheck ROMBind (Plus,$3190),(SE,$4BDE)
|
||
After2ndAccessBTInMountVol ROMBind (Plus,$2FFA),(SE,$4A4C)
|
||
AfterBTOpenInAccessBT ROMBind (Plus,$340A),(SE,$4E90)
|
||
AfterUpdateFreeInMtCheck ROMBind (Plus,$332E),(SE,$4DAE)
|
||
AfterReadBMInUpdateFree ROMBind (Plus,$59E0),(SE,$7488)
|
||
AfterGetBMBlkInReadBM ROMBind (Plus,$5A1E)
|
||
AfterGetBlockInGetBMBlk ROMBind (Plus,$5A9E)
|
||
|
||
CMRenameCN ROMBind (Plus,$6462),(SE,$7F2E),(II,$B9F2),(Portable,$D398),(IIci,$12Dcc) ;<37>
|
||
DtrmV2 ROMBind (Plus,$3788),(SE,$521A),(II,$8CDC),(IIci,$10056),(Portable,$A63E)
|
||
AfterVolCheckInRename ROMBind (Plus,$4498),(SE,$5F32),(II,$99FE),(IIci,$10DAA),(Portable,$B392)
|
||
RNmVol1 ROMBind (Plus,$45F8),(SE,$6092),(II,$9B5E),(IIci,$10F0A),(Portable,$B5F2)
|
||
RNmVol@70 ROMBind (Plus,$460E),(SE,$60B8),(II,$9B84),(IIci,$10F30),(Portable,$B518)
|
||
|
||
AfterCMSetupInCMCreateCN ROMBind (Plus,$604E), (SE,$7B1C), (II,$B5E0), (IIci,$129BA), (Portable, $0CF86) ;<27>
|
||
AfterCMSetupInCMDeleteCN ROMBind (Plus,$6194), (SE,$7c62), (II,$B726), (IIci,$12b00), (Portable, $0D0CC) ;<22>
|
||
AfterCMSetupInCMMoveCN ROMBind (Plus,$62c6), (SE,$7d92), (II,$B856), (IIci,$12c30), (Portable, $0D1FC) ;<22>
|
||
AfterCMSetupInCMRenameCN ROMBind (Plus,$646c), (SE,$7f38), (II,$B9FC), (IIci,$12dd6), (Portable, $0D3A2) ;<22>
|
||
AfterCMSetupInCMUpdateCN ROMBind (Plus,$6576), (SE,$803E), (II,$BB02), (IIci,$12EDC), (Portable, $0D4A8) ;<27>
|
||
AfterCMSetupInCMGetCN ROMBind (Plus,$6242), (SE,$7D0E), (II,$B7D2), (IIci,$12BAC), (Portable, $0D178) ;<23>
|
||
AfterCMGetCNInFndFilName ROMBind (Plus,$4A4E), (SE,$64F4), (II,$9FC0), (IIci,$1136C), (Portable, $0B954) ;<23>
|
||
AfterCMGetCNinRename ROMBind (Plus,$45ae), (SE,$6048), (II,$9B14), (IIci,$10EC0), (Portable, $0b4a8)
|
||
AfterCMRenameCNInRename ROMBind (Plus,$4554), (SE,$5ff2), (II,$9abe), (IIci,$10e6a), (Portable, $0b452) ;<22>
|
||
|
||
FlushVFiles ROMBind (Plus,$35Be), (SE,$5044), (II,$8B06), (IIci,$FE80), (Portable, $A468) ;<24>
|
||
FlUnMntCallsFlushBuffers ROMBind (Plus,$3622), (SE,$50A8), (II,$8B6A), (IIci,$FEE4), (Portable, $A4CC) ;<24>
|
||
FlUnMntAfterMFSCheck ROMBind (Plus,$3616), (SE,$509C), (II,$8B5E), (IIci,$FED8), (Portable, $A4C0) ;<24>
|
||
|
||
OfflineEjectCallsFlushBuffers ROMBind (Plus,$355E), (SE,$4FE4), (II,$8AA6), (IIci,$FDBE), (Portable, $A3AA) ;<24>
|
||
OfflineEjectCallsMFSFlush ROMBind (Plus,$3564),(SE,$4FEA), (II,$8AAC), (IIci,$FDC4), (Portable, $A3B0)
|
||
|
||
FlushBuffersAtflVCaches ROMBind (Plus,$34A0), (SE,$4F26), (II,$89E8), (IIci,$0FCFE), (Portable,$A2EA) ;<24>
|
||
FlushBuffersAtflBufExit ROMBind (Plus,$34D6), (SE,$4F5C), (II,$8A1E), (IIci,$0FD34), (Portable,$A320) ;<24>
|
||
FClose ROMBind (Plus,$5168), (SE,$6c0E), (II,$A6E4), (IIci,$11AA2), (Portable,$C08A)
|
||
DsposVBlks ROMBind (Plus,$336E), (SE,$4E28), (II,$88E4), (IIci,$0FBFA), (Portable,$A1B2)
|
||
DsposVCB ROMBind (Plus,$33A2), (SE,$4DF4), (II,$88B0), (IIci,$0FBC6), (Portable,$A1E6)
|
||
EjectIt ROMBind (Plus,$3598), (SE,$501E), (II,$8AE0), (IIci,$0FDFC), (Portable,$A3E4)
|
||
MFSFlush ROMBind (Plus,$3F00), (SE,$599A), (II,$945A), (IIci,$107D4), (Portable,$ADBC)
|
||
MarkVCBDirty ROMBind (Plus,$2D28), (SE,$4770), (II,$8218), (IIci, $0F52E), (Portable,$9B1A)
|
||
FlushMDB ROMBind (Plus,$2D2E), (SE,$4776), (II,$821E), (IIci, $0F534), (Portable,$9B20)
|
||
|
||
FOpen1 ROMBind (Plus,$4032), (SE,$5ACC), (II,$958C), (IIci,$10906), (Portable,$AEEE)
|
||
|
||
;________________________________________________________________________________
|
||
;
|
||
; Routine: HFS7.0 Enhancements Dispatch
|
||
;
|
||
; Function: Look at d0 and decide whether to dispatch to FileID handling routines,
|
||
; CatSearch, or the Desktop Manager. Also implements the local version
|
||
; of GetVolParms.
|
||
;
|
||
; The deal with $a060 trap numbers:
|
||
; 0-$1f HFS calls
|
||
; $20-$2f Desktop Manager Calls
|
||
; $30-$3f AppleShare calls (owned by AppleShare group)
|
||
; $40-$4f More appleshare calls
|
||
; $60-$7f More HFS calls
|
||
;________________________________________________________________________________
|
||
; This routine was rolled into TFSDispatch into the file TFS.a. <SM1>
|
||
HFSDispHook: PatchProc _HFSDispatch
|
||
|
||
; low and high values for the dispatch ranges we're patching
|
||
firstA60Disp equ selectCreateFileIDRef ; First entry is CreateFileIDRef, $14
|
||
lastA60Disp equ $6F ; Last entry is $6F, reserved for future use
|
||
|
||
import A60DispTbl ; all known A60 dispatch addresses
|
||
import OpenDF ; Open data fork
|
||
import AllocateFCBs
|
||
import OpenWDPatch, SetVolInfoPatch, UnknownCall
|
||
|
||
cmp.w #firstA60Disp, d0 ; dispatch less than 1st HFS 70 value?
|
||
blo.s @CheckLowStuff ; check for patches on more mature routines
|
||
cmp.w #lastA60Disp, d0 ; dispatch less than last known value?
|
||
bls.s @CoolDispatchSelector ; then go dispatch it
|
||
jmp UnknownCall ; then treat this call as unknown, possibly of
|
||
|
||
@CoolDispatchSelector:
|
||
move.w d0,d2 ; trap index into trashable reg <2.1>
|
||
sub.w #firstA60Disp, d2 ; 'normalize' dispatches to start at zero
|
||
leaResident A60DispTbl,a2 ; Point to the trap table
|
||
move.w d1, ioTrap(a0) ; Save unMunged trap word <14>
|
||
andi.w #$0f00, ioTrap(a0) ; Leave only modifiers (Async etc.) <14>
|
||
move.b d0, ioTrap+1(a0) ; Save the compacted trap information <14>
|
||
|
||
add.w d2,d2 ; double for word index
|
||
add.w d2,d2 ; double for long index
|
||
jmp (a2,d2.w) ; Pass control to routine
|
||
|
||
@CheckLowStuff:
|
||
; ever get the feeling we’ll have another dispatch table soon?
|
||
|
||
cmp.w #selectOpenWD, d0 ; openWD call?
|
||
bne.s @notOpenWD
|
||
jmp OpenWDPatch ; yes - do our special thing
|
||
@notOpenWD:
|
||
|
||
cmp.w #selectSetVolInfo, d0 ; SetVolInfo call? <20>
|
||
bne.s @notSetVolInfo
|
||
jmp SetVolInfoPatch ; yes - do our special thing (2) <20>
|
||
@notSetVolInfo:
|
||
|
||
jmpOld ; no - go call the next dude on the chain
|
||
endproc
|
||
|
||
macro
|
||
DispatchEntry &where
|
||
import &where
|
||
jmp &where
|
||
endm
|
||
|
||
A60DispTbl: proc
|
||
|
||
DispatchEntry CreateFileIDRef ; (0) dispatch $14
|
||
DispatchEntry DeleteFileIDRef ; (1) dispatch $15
|
||
DispatchEntry ResolveFileIDRef ; (2) dispatch $16
|
||
DispatchEntry PBHExchangeFiles ; (3) dispatch $17
|
||
DispatchEntry CMCatSearch ; (4) dispatch $18
|
||
DispatchEntry CheckCSPB ; (5) dispatch $19
|
||
DispatchEntry OpenDF ; (6) dispatch $1A
|
||
DispatchEntry MakeFSSpec ; (7) dispatch $1B
|
||
DispatchEntry UnknownCall ; (8) dispatch $1C
|
||
DispatchEntry UnknownCall ; (9) dispatch $1D
|
||
DispatchEntry UnknownCall ; (10) dispatch $1E
|
||
DispatchEntry UnknownCall ; (11) dispatch $1F
|
||
|
||
DispatchEntry DTGetPath ; (12) dispatch $20
|
||
DispatchEntry DTCloseDown ; (13) dispatch $21
|
||
DispatchEntry DTAddIcon ; (14) dispatch $22
|
||
DispatchEntry DTGetIcon ; (15) dispatch $23
|
||
DispatchEntry DTGetIconInfo ; (16) dispatch $24
|
||
DispatchEntry DTAddAPPL ; (17) dispatch $25
|
||
DispatchEntry DTRemoveAPPL ; (18) dispatch $26
|
||
DispatchEntry DTGetAPPL ; (19) dispatch $27
|
||
DispatchEntry DTSetComment ; (20) dispatch $28
|
||
DispatchEntry DTRemoveComment ; (21) dispatch $29
|
||
DispatchEntry DTGetComment ; (22) dispatch $2a
|
||
DispatchEntry DTFlush ; (23) dispatch $2b
|
||
DispatchEntry DTReset ; (24) dispatch $2c
|
||
DispatchEntry DTGetInfo ; (25) dispatch $2d
|
||
DispatchEntry DTOpenInform ; (26) dispatch $2e
|
||
DispatchEntry DTDelete ; (27) dispatch $2f
|
||
|
||
DispatchEntry GetVolParms ; (28) dispatch $30 GetVolParms
|
||
DispatchEntry VolumeCall ; (29) dispatch $31 GetLogInInfo
|
||
DispatchEntry VolumeCall ; (30) dispatch $32 GetDirAccess
|
||
DispatchEntry VolumeCall ; (31) dispatch $33 SetDirAccess
|
||
DispatchEntry VolumeCall ; (32) dispatch $34 MapID
|
||
DispatchEntry VolumeCall ; (33) dispatch $35 MapName
|
||
DispatchEntry VolumeCall ; (34) dispatch $36 CopyFile
|
||
DispatchEntry VolumeCall ; (35) dispatch $37 MoveRename
|
||
DispatchEntry OpenCall ; (36) dispatch $38 OpenDeny
|
||
DispatchEntry OpenCall ; (37) dispatch $39 OpenRFDeny
|
||
DispatchEntry VolumeCall ; (38) dispatch $3A Reserved for future use
|
||
DispatchEntry VolumeCall ; (39) dispatch $3B Reserved for future use
|
||
DispatchEntry VolumeCall ; (40) dispatch $3C Reserved for future use
|
||
DispatchEntry VolumeCall ; (41) dispatch $3D Reserved for future use
|
||
DispatchEntry VolumeCall ; (42) dispatch $3E Reserved for future use
|
||
|
||
DispatchEntry VolumeCall ; (43) dispatch $3F GetVolMountInfoSize
|
||
DispatchEntry VolumeCall ; (44) dispatch $40 GetVolMountInfo
|
||
DispatchEntry UnknownCall ; (45) dispatch $41 VolumeMount
|
||
DispatchEntry VolumeCall ; (46) dispatch $42 Share
|
||
DispatchEntry VolumeCall ; (47) dispatch $43 UnShare
|
||
DispatchEntry UnknownCall ; (48) dispatch $44 GetUGEntry
|
||
DispatchEntry UnknownCall ; (49) dispatch $45 ServerControl
|
||
DispatchEntry UnknownCall ; (50) dispatch $46 ServerStartup
|
||
DispatchEntry UnknownCall ; (51) dispatch $47 Reserved for future use
|
||
DispatchEntry UnknownCall ; (52) dispatch $48 Reserved for future use
|
||
DispatchEntry UnknownCall ; (53) dispatch $49 Reserved for future use
|
||
DispatchEntry UnknownCall ; (54) dispatch $4A Reserved for future use
|
||
DispatchEntry UnknownCall ; (55) dispatch $4B Reserved for future use
|
||
DispatchEntry UnknownCall ; (56) dispatch $4C Reserved for future use
|
||
DispatchEntry UnknownCall ; (57) dispatch $4D Reserved for future use
|
||
DispatchEntry UnknownCall ; (58) dispatch $4E Reserved for future use
|
||
DispatchEntry UnknownCall ; (59) dispatch $4F Reserved for future use
|
||
|
||
DispatchEntry GetParallelFCBFromRefnum ; (60) dispatch $50
|
||
DispatchEntry RefNumCall ; (61) dispatch $51 Reserved for future use
|
||
DispatchEntry RefNumCall ; (62) dispatch $52 Reserved for future use
|
||
DispatchEntry RefNumCall ; (63) dispatch $53 Reserved for future use
|
||
DispatchEntry RefNumCall ; (64) dispatch $54 Reserved for future use
|
||
DispatchEntry RefNumCall ; (65) dispatch $55 Reserved for future use
|
||
DispatchEntry RefNumCall ; (66) dispatch $56 Reserved for future use
|
||
DispatchEntry RefNumCall ; (67) dispatch $57 Reserved for future use
|
||
DispatchEntry RefNumCall ; (68) dispatch $58 Reserved for future use
|
||
DispatchEntry RefNumCall ; (69) dispatch $59 Reserved for future use
|
||
DispatchEntry RefNumCall ; (70) dispatch $5A Reserved for future use
|
||
DispatchEntry RefNumCall ; (71) dispatch $5B Reserved for future use
|
||
DispatchEntry RefNumCall ; (72) dispatch $5C Reserved for future use
|
||
DispatchEntry RefNumCall ; (73) dispatch $5D Reserved for future use
|
||
DispatchEntry RefNumCall ; (74) dispatch $5E Reserved for future use
|
||
DispatchEntry RefNumCall ; (75) dispatch $5F Reserved for future use
|
||
|
||
DispatchEntry VolumeCall ; (76) dispatch $60 GetAltPrivs
|
||
DispatchEntry VolumeCall ; (77) dispatch $61 SetAltPrivs
|
||
DispatchEntry DoFSCleanUp ; (78) dispatch $62 FSCleanUp
|
||
DispatchEntry AllocateFCBs ; (79) dispatch $63 AllocateFCBs
|
||
DispatchEntry VolumeCall ; (80) dispatch $64 Reserved for future use
|
||
DispatchEntry VolumeCall ; (81) dispatch $65 Reserved for future use
|
||
DispatchEntry VolumeCall ; (82) dispatch $66 Reserved for future use
|
||
DispatchEntry VolumeCall ; (83) dispatch $67 Reserved for future use
|
||
DispatchEntry VolumeCall ; (84) dispatch $68 Reserved for future use
|
||
DispatchEntry VolumeCall ; (85) dispatch $69 Reserved for future use
|
||
DispatchEntry VolumeCall ; (86) dispatch $6A Reserved for future use
|
||
DispatchEntry VolumeCall ; (87) dispatch $6B Reserved for future use
|
||
DispatchEntry VolumeCall ; (88) dispatch $6C Reserved for future use
|
||
DispatchEntry VolumeCall ; (89) dispatch $6D Reserved for future use
|
||
DispatchEntry VolumeCall ; (90) dispatch $6E Reserved for future use
|
||
DispatchEntry VolumeCall ; (91) dispatch $6F Reserved for future use
|
||
|
||
endproc
|
||
|
||
;________________________________________________________________________________
|
||
;
|
||
; Routine: Stub dispatch handlers
|
||
;
|
||
; Function: Pass a call to the external File System if it refers to an
|
||
; external File System volume.
|
||
;
|
||
; VolumeCall handles calls defined to take a vRefnum/ioNamePtr
|
||
; RefNumCall handles calls which take an FCB refnum
|
||
; OpenCall handles all forms of Open (which need to set up an FCB in d1)
|
||
; UnknownCall handles call for which there isn't yet a definition
|
||
;
|
||
; Inputs: A0 = Parameter block
|
||
; Output: D0 = error code, RfNumErr or zero.
|
||
;________________________________________________________________________________
|
||
; This routine was rolled into into the file TFS.a. <SM1>
|
||
VolumeCall: proc
|
||
jsrROM FSQUEUE ; Patiently wait our turn
|
||
jsrROM DTRMV3 ; Determine the volume being referred to
|
||
bne ToCmdDone ; Punt at the slightest hint of trouble
|
||
|
||
move.l a2,ReqstVol ; Point to the volume being referred to
|
||
tst.w VCBFSID(a2) ; External FS?
|
||
bne ExtFSErrExit ; Branch if it's not a local FS
|
||
bra ParamErrExit
|
||
|
||
;________________________________________________________________________________
|
||
;
|
||
; Routine: RefNumCall
|
||
;
|
||
; Function: Handle a refNum-based call. If the call refers to an External
|
||
; File System's volume, the call is passed to the external FS.
|
||
; Otherwise, a #paramErr is returned to the caller.
|
||
;________________________________________________________________________________
|
||
; This routine was rolled into into the file TFS.a. <SM1>
|
||
entry RefNumCall
|
||
RefNumCall:
|
||
import RefNumCheck
|
||
jsrROM FSQUEUE ; chill in line
|
||
move.w ioRefNum(a0),d0 ; Pick up the refNum
|
||
jsr RefNumCheck ; Check the refNum supplied
|
||
bne ToCmdDone ; Punt at first hint of trouble
|
||
|
||
movea.l FCBsPtr,a1 ; Point to the FCB table
|
||
move.w ioRefNum(a0),d1 ; Pick up the specified refNum
|
||
movea.l FCBVPtr(a1,d1),a1 ; Point to the VCB for this volume
|
||
move.l a1, ReqstVol ; In case it's #extFSErr
|
||
tst.w VCBFSID(a1) ; Local FS?
|
||
bne.s ExtFSErrExit ; No - let external file systems have it
|
||
bra.s ParamErrExit ; Local doesn't implement this call
|
||
|
||
;________________________________________________________________________________
|
||
;
|
||
; Routine: UnknownCall
|
||
;
|
||
; Function: Pass a call to the external File System. This routine is called for
|
||
; call selectors that are unknown to HFS and the Desktop Manager, but
|
||
; which might be implemented for External File Systems.
|
||
;
|
||
; Inputs: A0 = Parameter block
|
||
; Output: D0 = error code, RfNumErr or zero.
|
||
;________________________________________________________________________________
|
||
; This routine was rolled into into the file TFS.a. <SM1>
|
||
entry UnknownCall
|
||
UnknownCall:
|
||
jsrROM FSQUEUE ; Wait our turn
|
||
; <REALLY Fudge ReqsVol here later...>
|
||
movea.l VCBQHdr+qHead, a2 ; Pick up pointer to first VCB
|
||
move.l a2, ReqstVol ; Point someplace semi-innocuous
|
||
bra.s ExtFSErrExit ; ... and pass it along
|
||
|
||
;________________________________________________________________________________
|
||
;
|
||
; Routine: OpenCall
|
||
;
|
||
; Function: Open a fork with specific Deny modes; pass the call to the External;
|
||
; file system if it's for an Ext. FS volume.
|
||
;
|
||
; Inputs: A0 = Parameter block
|
||
; Output: D0 = error code, RfNumErr or zero.
|
||
;________________________________________________________________________________
|
||
; This routine was rolled into into the file TFS.a. <SM1>
|
||
entry OpenCall
|
||
OpenCall:
|
||
import OpenAttemptHook
|
||
jsrROM FSQUEUE ; Wait our turn
|
||
jsrROM DTRMV3 ; Determine the volume being referred to
|
||
bne.s ToCmdDone ; Punt at the slightest hint of trouble
|
||
|
||
move.l a2,ReqstVol ; Point to the volume being referred to
|
||
tst.w VCBFSID(a2) ; External FS?
|
||
beq.s ParamErrExit ; It's an unknown trap for a local volume
|
||
|
||
bsr OpenAttemptHook ; let ourselves know we're trying <38>
|
||
jsrROM GT1STFCB ; get (A1,D1) pointing to first FCB
|
||
@1 tst.l FCBFlNm(a1,d1) ; FCB unused
|
||
beq.s @90 ; br if so
|
||
jsrROM GTNXTFCB ; get next one until we run out
|
||
bcs.s @1
|
||
|
||
moveq.l #TMFOErr,d0 ; too many files open
|
||
clr.w IORefNum(a0) ; make refnum invalid
|
||
bra.s ToCmdDone
|
||
|
||
@90 add.l a1,d1 ; Point to the FCB
|
||
|
||
ExtFSErrExit:
|
||
moveq.l #ExtFSErr,d0 ; Good news!
|
||
bra.s ToCmdDone
|
||
|
||
ParamErrExit:
|
||
moveq.l #paramErr, d0 ; It's an unknown trap for a local volume
|
||
|
||
ToCmdDone:
|
||
jmpROM CMDDONE ; And call it a day.
|
||
|
||
endproc
|
||
|
||
;________________________________________________________________________________
|
||
;
|
||
; Routine: RefNumCheck
|
||
;
|
||
; Function: Check the refNum supplied to make sure it's a real file RefNum.
|
||
;
|
||
; Inputs: d0 = refnum
|
||
; Output: d0 = error code, RfNumErr or noErr
|
||
;
|
||
; Note:
|
||
; This routine is called by the desktop manager and the UnTagFCB _Close patch.
|
||
;________________________________________________________________________________
|
||
; This routine was rolled into into the file TFS.a. <SM1>
|
||
RefNumCheckRegs reg a1/d1
|
||
RefNumCheck: proc export
|
||
movem.l RefNumCheckRegs, -(sp)
|
||
moveq.l #0, d1 ; clear high word
|
||
move.w d0, d1 ; make a copy of the refnum
|
||
bmi.s @RefNumErrExit ; no negative refnums
|
||
|
||
divu FSFCBLen,d1 ; Divide by the FCB size
|
||
swap d1 ; Get remainder in low word
|
||
subq.w #2,d1 ; Is the remainder 2?
|
||
bne.s @RefNumErrExit ; If not, no way this could be a refnum
|
||
|
||
move.l FCBSPtr, a1 ; point to length word of FCB array
|
||
cmp.w (a1), d0 ; check against size of FCB array
|
||
bgt.s @RefNumErrExit ; Too high? Too bad.
|
||
|
||
moveq.l #noErr, d0 ; No pwobwem!
|
||
bra.s @Exit
|
||
|
||
@RefNumErrExit:
|
||
moveq.l #RfNumErr,d0 ; Indicate a bad refNum
|
||
@Exit:
|
||
movem.l (sp)+, RefNumCheckRegs
|
||
tst.w d0 ; Set condition codes
|
||
rts
|
||
endproc
|
||
|
||
;________________________________________________________________________________
|
||
;
|
||
; Routine: OpenDF
|
||
;
|
||
; Function: Opens a data fork. Identical to Open, except that no checking
|
||
; for drivers is done.
|
||
;
|
||
; Changes the trap word to _Open (from _HFSDispatch) so that external
|
||
; file systems will work properly. Preserves hfs and async bits
|
||
;________________________________________________________________________________
|
||
; This routine was rolled into into the file TFSDIR1.a. <SM1>
|
||
OpenDF: proc
|
||
move.b #0, d1 ; clear low byte $Ax60 -> $Ax00
|
||
move.w d1, ioTrap(a0) ; clean up the pb, too <38>
|
||
move.l jFileOpen, -(sp) ; get the vector straight into the file system
|
||
rts ; jump in with both feet
|
||
endproc
|
||
|
||
;_______________________________________________________________________
|
||
;
|
||
; Routine: SetFilLock, RstFilLock
|
||
;
|
||
; (c) 1983, 1985, 1990 Apple Computer, Inc.
|
||
;
|
||
; Arguments: A0.L (input) -- pointer to I/O parameter block, uses
|
||
; IOFileName,IOFileType,IODrvNum
|
||
; D0.W (output) -- 0 if file was found and set correctly.
|
||
; Calls: FndFilName
|
||
; Function: Set or reset the file lock bit.
|
||
;
|
||
; Modification History:
|
||
; 16 Jan 83 LAK New today.
|
||
; 06 Jun 83 LAK Broke out CkFilMod as a subroutine.
|
||
; 10 Mar 85 PWD Modified for use with TFS catalog entries
|
||
; 28 Jun 90 dnf Modified to set/clear afp inhibit bits
|
||
;
|
||
; Note: if the file is locked after it has already been opened with write
|
||
; permission, the lock will not affect that opened file. If the lock bit
|
||
; is not changed by this command, the directory buffer is not marked dirty
|
||
; (allows quick lock/unlocking of a group of file, regardless of their current
|
||
; status).
|
||
;_______________________________________________________________________
|
||
; This patch was rolled back into TFSDIR3.A <SM1>
|
||
MakePatch SetFilLock,_SetFilLock
|
||
MakePatch RstFilLock,_RstFilLock
|
||
|
||
SetFilLock: proc export
|
||
export RstFilLock
|
||
|
||
JSRRom FSQUEUE ; wait our turn
|
||
ST FLckUnlck ; want to lock it
|
||
BRA.S SRFLck
|
||
|
||
RstFilLock:
|
||
jsrROM FSQUEUE
|
||
CLR.B FLckUnlck
|
||
|
||
SRFLck jsrROM romCkFilMod ; look for file and see if we can mod it
|
||
; (doesn't return on errors)
|
||
JSRROM TFSVCBTST ; Are we dealing with a TFS volume? <01Oct85>
|
||
BNEROM romChgMFlLock ; Nope - do it the old fashioned way
|
||
MOVEQ #FNFErr,D0 ; Expect the worst
|
||
CMP.B #cdrFilRec,cdrType(A5) ; Is this a file entry?
|
||
BNE.S SRFLXit3 ; No - don't touch it
|
||
TST.B FLckUnlck ; Yes! Now, what was it we wanted?
|
||
BEQ.S @1 ; br if we want to unlock it
|
||
BSET #FilWrtFlag,FilFlags(A5) ; lock it
|
||
BNE.S SRFLXit2 ; br if already locked
|
||
ori.b #xFFFilAttrLockMask, filFndrInfo+fdXFlags(a5) ; set bits in fdXFlags <28Jun90> <15>
|
||
BRA.S SRFLXit1 ; otherwise, it's been modified
|
||
|
||
@1 BCLR #FilWrtFlag,FilFlags(A5) ; unlock it
|
||
BEQ.S SRFLXit2 ; br if already unlocked
|
||
andi.b #~xFFFilAttrLockMask, filFndrInfo+fdXFlags(a5) ; clear bits in fdXFlags <28Jun90> <15>
|
||
|
||
; Mark the buffer holding the catalog entry dirty:
|
||
|
||
SRFLXit1 MOVE.L D7,D2 ; Get a hold of the catalog hint
|
||
JSRROM romCMUpdateCN ; Update the Catalog buffer
|
||
BRA.S SRFLXit3 ; If EQ, we're all set.
|
||
|
||
SRFLXit2 MOVEQ #0,D0
|
||
SRFLXit3 jmpROM CMDDONE
|
||
|
||
endproc
|
||
|
||
;________________________________________________________________________________
|
||
; <55>
|
||
; Routine: GetParallelFCBFromRefnum
|
||
; Function: Return a pointer to the parallel FCB entry for a given
|
||
; file RefNum.
|
||
;
|
||
; Input: ioRefNum(a0) - File reference number
|
||
;
|
||
; Output: ioMisc(a0) - Pointer to parallel FCB entry
|
||
;________________________________________________________________________________
|
||
; This routine was rolled into TFSDIR1.a
|
||
GetParallelFCBFromRefnum proc export
|
||
import ParallelFCBFromRefnum
|
||
import RefNumCheck
|
||
|
||
jsrROM FSQUEUE ; wait our turn
|
||
|
||
move.w ioRefNum(a0),d0 ; Get the file reference number
|
||
move.w d0,d1 ; Keep a copy in another register
|
||
bsr RefNumCheck ; Make sure the reference number is valid
|
||
bmi.s @exitGetParallelFCB ; Bad reference number
|
||
|
||
move.w d1,d0 ; Get the copy
|
||
bsr ParallelFCBFromRefnum ; Get the pointer to the parallel structure
|
||
move.l a1,ioFDirIndex(a0) ; Return the pointer in ioMisc
|
||
moveq #noErr,d0 ; No error
|
||
@exitGetParallelFCB
|
||
jmpROM CMDDONE ; outa here
|
||
endproc
|
||
|
||
;________________________________________________________________________________
|
||
;
|
||
; Routine: ParallelFCBFromRefnum
|
||
; Function: Return a pointer to the parallel FCB entry for a given
|
||
; file RefNum.
|
||
;
|
||
; Input: d0.w - file refNum
|
||
;
|
||
; Output: d0.l - trash
|
||
; a1.l - pointer to parallel FCB entry
|
||
;
|
||
; Note: This routine assumes that d0 is really a decent, law-abiding
|
||
; refnum. RefNumCheck is a fine way to assure this.
|
||
;________________________________________________________________________________
|
||
; This routine was rolled into TFSDIR1.a
|
||
ParallelFCBFromRefnum proc export
|
||
andi.l #$FFFF, d0 ; only the low word is passed. <11>
|
||
divu.w FSFCBLen,d0 ; convert refnum to an index
|
||
movea.l FSVarsPtr,a1 ; get address of HFS variable area
|
||
movea.l FSVars.fcbPBuf(a1),a1 ; get address of parallel array
|
||
mulu.w cbPBufULen(a1),d0 ; convert file index to parallel array offset
|
||
lea fcbPBufData(a1,d0.l),a1 ; a1 -> parallel array element for file <20>
|
||
rts
|
||
endproc
|
||
|
||
;________________________________________________________________________________
|
||
; <20>
|
||
; Routine: ParallelWDCBFromRefnum
|
||
; Function: Return a pointer to the parallel FCB entry for a given
|
||
; working directory RefNum.
|
||
;
|
||
; Input: d0.w - working directory refNum
|
||
;
|
||
; Output: d0.l - trash
|
||
; a1.l - pointer to parallel WDCB entry
|
||
;
|
||
; Note: This routine assumes that d0 is really a decent, law-abiding
|
||
; refnum. RefNumCheck is a fine way to assure this.
|
||
;________________________________________________________________________________
|
||
; This routine was rolled into TFSDIR1.a
|
||
ParallelWDCBFromRefnum proc
|
||
subi.w #WDRfnMin,d0 ; Change WDRefNum to WDCB table offset
|
||
assert WDCBLen = 1<<4 ; ensure that this shift is still legal
|
||
lsr.w #4,d0 ; convert WDCB table offset to an index
|
||
movea.l FSVarsPtr,a1 ; get address of HFS variable area
|
||
movea.l FSVars.wdcbPBuf(a1),a1 ; get address of parallel array
|
||
mulu.w cbPBufULen(a1),d0 ; convert WDCB index to parallel array offset
|
||
lea wdcbPBufData(a1,d0.l),a1 ; a1 -> parallel array element for file
|
||
rts
|
||
endproc
|
||
|
||
;————————————————————————————————————————————————————————————————————————————————————————————————————
|
||
; <16>
|
||
; Routine: ClosePatch
|
||
;
|
||
; Function: Prevent a file _Close from occuring on a file that was opened by kSystemProcess
|
||
; unless we're currently running kSystemProcess. This keeps random people (apps with
|
||
; housekeeping code that closes lots of files, for example) from closing system-like
|
||
; files out from under us.
|
||
;
|
||
;————————————————————————————————————————————————————————————————————————————————————————————————————
|
||
; This patch was rolled into TFSRFN2.a
|
||
ClosePatch PatchProc _Close
|
||
|
||
@ClosePatchRegs reg a0-a2/d0-d1
|
||
|
||
movem.l @ClosePatchRegs,-(sp) ; save regs we use
|
||
movea.l sp,a2 ; for safe, quick stack pointer restore
|
||
|
||
; check the refnum for validity. and locate the parallel FCB array element
|
||
move.w ioRefNum(a0),d0 ; get refnum being closed
|
||
bsr RefNumCheck ; is it legit?
|
||
bne.s @DoClosePop ; if not, let the real trap deal with it
|
||
move.w ioRefNum(a0),d0 ; get refnum being closed
|
||
bsr ParallelFCBFromRefnum ; a1 = address of parallel FCB element
|
||
|
||
; check whether we have tagged the file -- this makes sure that we can even call the
|
||
; Process Mgr (since files are tagged only when the Process Mgr is available), but is
|
||
; cheaper than ProcessMgrExists
|
||
tst.l xFCBPid2(a1) ; is low long initialized?
|
||
bne.s @NeedToCheck ; jump if so, we need to make the trap calls
|
||
tst.l xFCBPid1(a1) ; is high long initialized?
|
||
beq.s @DoClosePop ; both are uninitialized, OK to close!
|
||
|
||
; the quicker of the two checks is whether file opened by kSystemProcess, do it first
|
||
@NeedToCheck
|
||
; WRONG WRONG WRONG ; <53> <53> <53>
|
||
; pea #kSystemProcess ; push low long of PSN
|
||
; ----------------------------------------------------------
|
||
pea kSystemProcess ; push low long of PSN
|
||
|
||
clr.l -(sp) ; push high long of PSN
|
||
clr.l -(sp) ; allocate storage for boolean and result
|
||
pea 4(sp) ; push address of kSystemProcess
|
||
pea xFCBPid1(a1) ; push address of PSN from parallel FCB
|
||
pea 10(sp) ; push address of Boolean
|
||
_SameProcess ; compare FCB PSN to kSystemProcess
|
||
addq.l #2,sp ; dump OSErr (Boolean OK on error)
|
||
tst.w (sp) ; check Boolean (still false if error)
|
||
beq.s @DoClosePop ; jump if FCB not opened by kSystemProcess
|
||
|
||
; OK, file was opened by kSystemProcess, see whether _Close caller is also kSystemProcess
|
||
clr.w (sp) ; set Boolean to "false", for easier check later
|
||
suba.l #10,sp ; allocate storage for PSN (8 bytes) and result
|
||
pea 2(sp) ; push address of PSN storage
|
||
_GetSystemClientProcess ; get party responsible for this _Close call
|
||
pea 2(sp) ; push address of client PSN
|
||
pea 16(sp) ; push address of kSystemProcess
|
||
pea 18(sp) ; push address of Boolean
|
||
_SameProcess ; is current client same as kSystemProcess?
|
||
tst.w 10(sp) ; check boolean
|
||
beq.s @DontClosePop ; if "false", caller is not kSystemProcess
|
||
|
||
; file not opened by kSystemProcess, or closer is also kSystemProcess
|
||
@DoClosePop
|
||
movea.l a2,sp ; restore stack
|
||
movem.l (sp)+,@ClosePatchRegs ; restore regs we used
|
||
jmpOld ; close the file
|
||
|
||
; file was opened by kSystemProcess, but is being closed by someone else. Just say no!
|
||
@DontClosePop
|
||
movea.l a2,sp ; restore stack
|
||
movem.l (sp)+,@ClosePatchRegs ; restore regs we used
|
||
moveq.l #permErr,d0 ; set error code
|
||
move.w d0,ioResult(a0) ; agree in the iopb
|
||
rts ; return to caller
|
||
|
||
endproc
|
||
|
||
;————————————————————————————————————————————————————————————————————————————————————————————————————
|
||
; Fix the DiskSwitch alert so disk eject FKEYs 0,1 and 2 will work.
|
||
;————————————————————————————————————————————————————————————————————————————————————————————————————
|
||
fixDiskSwitch ComeFromPatchProc _GetOSEvent, FromWaitForDsk,(Plus,SE,II)
|
||
|
||
move.l a0, -(sp) ; Save the event record.
|
||
JsrOld ; Call the old routine.
|
||
move.l (sp)+, a0 ; Restore the event record.
|
||
moveq.l #keyDwnEvt,d1
|
||
cmp.w evtNum(a0),d1 ; Is it a key down?
|
||
bne.s @notKeyDwn ; nope.
|
||
|
||
; It's a key down, so munge the message so that the ROM code will accept it.
|
||
cmp.b #'0', evtMessage+3(a0) ; does it look like a number? <11>
|
||
blt.s @notKeyDwn ; if less than '0', don’t adjust! <11>
|
||
sub.b #'0', evtMessage+3(a0) ; convert from ascii
|
||
|
||
@notKeyDwn
|
||
rts
|
||
|
||
EndProc
|
||
|
||
;————————————————————————————————————————————————————————————————————————————————————————————————————
|
||
; DSHook — use SysErrorUpdateRect
|
||
|
||
; There is a bug in the DSHook code used to force updates that is used when there is insufficient
|
||
; room on the stack to save the room behind the disk switch dialog. This patch causes the DSHook to
|
||
; use a new global, SysErrorUpdateRect, instead. This gets rid of the bug where the high bit of the
|
||
; top coordinate was always set, and it also allows others to share this update mechanism. This was
|
||
; impossible before, since any disk switches would clobber the DSAlertRect global, which was used
|
||
; both by the deep-shit error handler, and by the updating code in GetNextEvent. See the patch to
|
||
; GetNextEvent that goes with this for more information.
|
||
;————————————————————————————————————————————————————————————————————————————————————————————————————
|
||
; The functionality of these patches was rolled into TFS.a <SM1>
|
||
ForceSetPortInDiskSwap comefrompatchproc _SysError,AfterSysErrorInDSHook
|
||
|
||
tst.l d4 ; check and see if we had a port saved
|
||
bnzOld ; if we had one saved, then SetPort will be called
|
||
moveq #1,d4 ; put a non-zero value in for the port, this forces a call to our SetPort patch
|
||
braOld
|
||
|
||
endproc
|
||
|
||
AccumulateDiskSwitchRectIntoUpdateRect comefrompatchproc _SetPort, DSExit
|
||
|
||
tst.l d5 ; check if we need to post an update
|
||
bnz.s @noUpdate ; nope, we did saving
|
||
|
||
pea DSAlertRect ; union this in
|
||
move.l ExpandMem,a0
|
||
pea ExpandMemRec.SysErrorUpdateRect(a0) ; into this rect
|
||
move.l (sp),-(sp) ; same rect is destination
|
||
_UnionRect
|
||
|
||
@noUpdate
|
||
|
||
subq.l #1,d4 ; did I set this to 1 earlier?
|
||
bneOld ; no, continue with the real SetPort
|
||
|
||
move.l (sp)+,(sp) ; get rid of the parameter to SetPort
|
||
rts ; return to DSHook
|
||
|
||
endproc
|
||
|
||
;________________________________________________________________________________
|
||
; Rename/SetVolInfo - fix bdNamErr, SetVolInfo to '', and SetVolInfo update catalog
|
||
;
|
||
; Neither of these calls returns #bdNamErr when prospective volume names are > 27
|
||
; characters. There are three cases:
|
||
; 1) Rename of a volume (specified as a volume)
|
||
; 2) Rename of a root directory (specified as a parId/cName)
|
||
; 3) SetVolInfo of a volume
|
||
;
|
||
; We also check for cases where SetVolInfo would rename a volume to ''.
|
||
; We also rename the root directory when SetVolInfo renames the volume.
|
||
;
|
||
; There are 3 patches for the three cases above, but the SetVolInfo patch
|
||
; fixes two other bugs, for a total of 3 patches, and 3 bugs.
|
||
;
|
||
; The patches for cases 1 and 3 are here.
|
||
; The patch for case 2 is tacked on to the File IDs patch in CMSvcsExtras.
|
||
; That way we get all the preflighting that the guts of Rename does, and we
|
||
; can just do the check and bail if necessary.
|
||
;
|
||
; This patch also includes Patch #40 (Volume Rename patch) from PatchPlusROM.a
|
||
; The original comment follows:
|
||
; This patch fixes a problem in the File System's Rename code - when a
|
||
; volume rename is detected, the File System doesn't check whether the
|
||
; volume being renamed belongs to an External File System and ends up
|
||
; attempting to make a CMRenameCN call for a volumes for which it has no
|
||
; access to the catalog B*-Tree, resulting in a hung system.
|
||
;________________________________________________________________________________
|
||
; <20>
|
||
; Here we fix case 1 - rename of a volume. Fixed at the head of Rename:
|
||
; This patch was rolled into TFSDIR2.a <SM1>
|
||
FixVolumeRenames PatchProc _Rename
|
||
jsrROM FSQUEUE ; what a queue command!
|
||
|
||
move.l ioNewName(a0),d2 ; check out the new name
|
||
jsrROM DtrmV2 ; find what volume the new name is on
|
||
bne.s RNmVol ; br if error (may want to rename vol)
|
||
tst.w d2 ; check name length
|
||
beq.s RNmVol1 ; if zero, may be volume-conflict rename
|
||
jmpROM AfterVolCheckInRename
|
||
|
||
; ... lots of Rename work happens in ROM ...
|
||
; (that has nothing to do with the volume case, but includes the root dir case)
|
||
|
||
; ... User is trying to rename a volume. Give it a try ...
|
||
|
||
RNmVol:
|
||
cmp.w #nsvErr,d0 ; make sure it's no-such-vol error
|
||
bneROM CMDDONE
|
||
tst.w d2 ; name length should be zero
|
||
bne.s BadNewNam
|
||
RNmVol1:
|
||
cmp.w #vcbMaxNam, d3 ; is the proposed name too long? <20>
|
||
bhi.s BadNewNam ; > 27 is just right out
|
||
jmpROM RNmVol1 ; go let'em do it like they used to
|
||
|
||
tst.w d3 ; Volume name must have been specified
|
||
beqROM CMDDONE
|
||
jsrROM DTRMV3 ; Check source volume
|
||
bneROM CMDDONE ; Punt if no such thing
|
||
jsrROM CVFLGS ; Can it be modified?
|
||
bneROM CMDDONE ; Nope - punt now.
|
||
jsrROM TFSVCBTST ; Is it a TFS volume?
|
||
beq.s @checkExtFS ; Yes - make sure it's not ext. FS
|
||
tst.w vcbDrvNum(a2) ; Volume on-line?
|
||
beqROM CMDDONE ; No - just forget it
|
||
jmpROM RNMVol@70 ; Yes - go and rename it
|
||
|
||
@checkExtFS:
|
||
jsrROM EXTOFFLINCK ; Our file system, on-line volume?
|
||
bneROM CMDDONE ; If not, get out now
|
||
jmpROM RNMVol@70 ; Otherwise, same as ever
|
||
|
||
BadNewNam:
|
||
moveq.l #bdNamErr,d0 ; get the error code
|
||
jmpROM CMDDONE ; outa here
|
||
|
||
endproc
|
||
|
||
; Here we fix case 3 - SetVolInfo.
|
||
; Of course, we also patch out the entire SetVolInfo routine, and fix the
|
||
; bugs where we formerly didn’t rename the root directory and allowed a '' name.
|
||
|
||
SetVolInfoPatch proc
|
||
jsrROM FSQUEUE ; Wait our turn
|
||
jsrROM romDtrmV1 ; Determine volume by drvnum/refnum
|
||
bne SVIDone ; Punt on errors
|
||
jsrROM CVFLGS ; Check if volume can be modified
|
||
bne SVIDone ; Give up easily
|
||
jsrROM EXTOFFLINCK ; Check if volume is on-line, non ext. FS
|
||
bne SVIDone ; Be a wimp - give up now.
|
||
|
||
move.l ioVNPtr(a0),d2 ; New name specified?
|
||
beq.s DoneNameCheck ; If not, we can handle it
|
||
movea.l d2,a1 ; Point to new name
|
||
moveq.l #0,d2 ; start with first character and assume no name
|
||
move.b (a1), d3 ; empty string?
|
||
beq.s DoneNameCheck ; handle just like no name specified
|
||
|
||
@nextChar:
|
||
addq.b #1,d2 ; Advance to the next character
|
||
cmp.b #':',0(a1,d2.w) ; Hit a colon?
|
||
beq.s @colon ; Yes - punt.
|
||
cmp.b #vcbMaxNam,d2 ; Exceeded the max. length?
|
||
bgt.s @badName ; Yes - name is too long.
|
||
cmp.b d3,d2 ; At last character?
|
||
beq.s DoneNameCheck ; If so, we're not too long and no colons
|
||
bra.s @nextChar ; no - look for another
|
||
@colon:
|
||
cmp.b d3, d2 ; was colon the last character?
|
||
bne.s @badName ; last char is the only legal spot
|
||
subq.w #1, d2 ; was colon the only character?
|
||
bne.s DoneNameCheck ; must have at least one other
|
||
|
||
@badName:
|
||
moveq.l #bdNamErr,d0 ; get the error code
|
||
bra SVIDone ; we are finished, that’s for sure
|
||
|
||
DoneNameCheck:
|
||
; At this point, d2 is flag on the quality of the name
|
||
; if d2.b is non-zero, then we need to set the name
|
||
|
||
; Set user-modifiable fields in the VCB:
|
||
SetUserFields:
|
||
move.l ioVCrDate(a0),vcbCrDate(a2) ; Volume creation date
|
||
move.l ioVLsMod(a0),vcbLsMod(a2) ; Date of last modification
|
||
move.w ioVAtrb(a0),d0 ; Volume attributes
|
||
and.w #$8000,d0 ; Clear all but vol. locked bit
|
||
move.w vcbAtrb(a2),d1 ; Current volume attributes
|
||
and.w #$7FFF,d1 ; Clear vol. locked bit
|
||
or.w d0,d1 ; Combine attributes
|
||
move.w d1,vcbAtrb(a2) ; Set new volume attributes
|
||
|
||
move.l ioVNPtr(a0), d1 ; grab ptr to caller's name
|
||
|
||
jsrROM TFSVCBTST ; Is it a TFS volume?
|
||
bne.s @skipHFS ; br if not - MFS VCBs don't have this info
|
||
move.l ioVClpSiz(a0),vcbClpSiz(a2) ; Volume clump size
|
||
move.l ioVBkup(a0),vcbVolBkup(a2) ; Last backup date
|
||
move.w ioVSeqNum(a0),vcbVSeqNum(a2); Volume sequence number
|
||
lea.l ioVFndrInfo(a0),a0 ; Point to finder info source
|
||
lea.l vcbFndrInfo(a2),a1 ; Finder info in VCB
|
||
moveq.l #32,d0 ; Length of finder info
|
||
_BlockMove ; Copy it in
|
||
@skipHFS:
|
||
|
||
tst.b d2 ; do we want to rename
|
||
beq.s @skipRename ; if not, we're already done
|
||
|
||
move.l d2, d0 ; get psyched for _BlockMove
|
||
move.l d1, a0 ; recover ptr to caller's name
|
||
addq.w #1, a0 ; skip caller’s length byte
|
||
lea.l vcbVN(a2), a1 ; VCB's copy
|
||
move.b d0, (a1)+ ; jam our length byte
|
||
_BlockMove ; change the name
|
||
|
||
jsrROM TFSVCBTST ; Are we dealing with an MFS volume?
|
||
bne.s @skipRootRename ; if so, no root to rename
|
||
|
||
; rename the root
|
||
moveq.l #0,d2 ; No catalog hint for rename
|
||
moveq.l #fsRtDirID,d0 ; Root directory
|
||
movea.l d2,a0 ; Nil CName pointer
|
||
lea.l vcbVN(a2), a1 ; VCB's copy
|
||
jsrROM CMRenameCN ; Rename the catalog root directory
|
||
bne.s SVIDone ; we're done, success or not
|
||
@skipRootRename:
|
||
@skipRename:
|
||
|
||
jsrROM romMarkVCB ; Mark VCB as dirty
|
||
moveq.l #noErr,d0 ; All went well...
|
||
|
||
SVIDone:
|
||
jmpROM CMDDONE
|
||
endproc
|
||
|
||
;__________________________________________________________________________________
|
||
; <22>
|
||
; Patch to CMSetUp
|
||
; We patch CMSetUp to trigger the following patches:
|
||
; deleteCN keep file threads current
|
||
; moveCN keep file threads current
|
||
; renameCN keep file threads current
|
||
; return #bdNamErr on renames of the root dir > 27 chars
|
||
; (this is case 2 from above)
|
||
; getCN distinguish between #fnfErr and #dirNFErr for DirCreate
|
||
; and Create.
|
||
;
|
||
; The renameCN and getCN patches have two level triggers, i.e. we watch for
|
||
; calls to CMSetUp from CMRename from PBRename.
|
||
;__________________________________________________________________________________
|
||
; The Disappearing Folder patch was rolled into TFSDIR2.a <SM1>
|
||
myCMSetUp PatchProc jCMSetup,(Plus,SE,II,IIci,Portable)
|
||
Import CMDeleteCNAfterSetup
|
||
Import CMMoveCNAfterSetup
|
||
Import CMRenameCNAfterSetup
|
||
Import CMRenameCNExit1
|
||
Import CMCreateUpdateCNPatch ; mark VCB dirty fix <10Jan91 #27>
|
||
|
||
|
||
* To fix the Disappearing Folder bug <18Mar92>
|
||
* pp, stb, jv, bb, gs, et al.
|
||
*
|
||
* High-level _Rename will attempt to adjust the FCB of the object it just renamed if that object is in
|
||
* the FCB list (meaning that the "file" is open). Unfortunately, folders don’t have FCB’s. Actually,
|
||
* ignoring my sarcasm, folders are not supposed to have them, and the unfortunate thing is that _Rename
|
||
* doesn’t check before trying to update an FCB. Normally, _Rename would not be able to match a folder
|
||
* with an FCB, but there are opportunities where it can fool itself into thinking it has matched. When
|
||
* that happens, it alters some poor file’s FCB. This is bad. Once the FCB list has been damaged, all
|
||
* sorts of things can go wrong, including the loss of files and folders. The details are interesting,
|
||
* but the important thing is to make sure the FCBs don’t get trashed, and that’s what this patch does.
|
||
|
||
cmpROM AfterCMGetCNinRename,$10(a6) ; was it ROM Rename?
|
||
beq.S @RenameAfterCMGetCN ; not what were looking for
|
||
BNE.S @NotAfterCMGetCNinRename
|
||
|
||
@RenameAfterCMGetCN
|
||
|
||
; the story so far...
|
||
|
||
; in Rename from the update FCB section
|
||
;
|
||
; movem.l FSTemp8,D2/A4
|
||
; bsr PushCName
|
||
; move.l D2,-(A6)
|
||
; move.l D6,D0
|
||
; move.l D7,D2
|
||
; jsr CMGetCN
|
||
;
|
||
; in CMGetCN
|
||
;
|
||
; move.l (sp)+,-(A6) ; save return address on A6 stack
|
||
; movem.l D1/D3/A3-A4,-(A6) ; save regs
|
||
; bsr CMSetUp ; common set up routine
|
||
;
|
||
; in CMSetUp
|
||
;
|
||
; move.l jCMSetUp,-(sp)
|
||
; rts
|
||
|
||
; now we have control, hah, hah, hah, hah, haaaaaah!
|
||
|
||
; here's what CMSetUp would have done for us
|
||
|
||
suba.w #lenCMVars,A6 ; allocate memory for CM vars on HFS stack
|
||
movea.l A6,A4 ; A4 = pointer to CM vars
|
||
addq.w #4,A7 ; pop return address to CMGetCN, instead of rts
|
||
|
||
; then we would return to this code in CMGetCN
|
||
|
||
clr.l VCBDirIDM(A2) ; invalidate current DirID marker
|
||
jsrROM romLocCNode ; locate dir/file BTree rec for the CNode (sets up A1)
|
||
|
||
@GCExit
|
||
adda.w #lenCMVars,A6 ; de-allocate memory for CM vars
|
||
movem.l (A6)+,D1/D3/A3-A4 ; restore regs
|
||
move.l (A6)+,-(sp) ; put return address back on stack
|
||
|
||
; ••••••••••••••••••••••••••••••••••••••• Here’s the fix ••••••••••••••••••••••••••••••••••••••••
|
||
; I've got an idea! Why don't we check to see if this is a file or a directory
|
||
cmpi.b #cdrDirRec,cdrType(A1) ; if it's a directory
|
||
beq @BugOut ; Hey! we shouldn't even be here...
|
||
; •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
|
||
|
||
tst.w D0 ; set condition codes
|
||
rts ; return to Rename as if from CMGetCN
|
||
|
||
@BugOut ; clean up A6 & A7 stacks and jump back to rom
|
||
addq.w #4,A7 ; remove return addr to Rename
|
||
move.l (A6)+,D2 ; recover rounded string length
|
||
jsrRom POPCNAME ; remove name from A6 stack
|
||
|
||
@RNmExit cmp.w #BTExists,D0 ; B*-Tree error?
|
||
bne.s @FinalExit
|
||
moveq #dupFNErr,D0 ; If entry exists, give better error code
|
||
@FinalExit jmpRom CMDDONE ; and now back to our regularly scheduled broadcast...
|
||
|
||
|
||
; CMSvcs calls "MarkVCBTime", which only set the mod time but doesn't mark VCB dirty.
|
||
; This leaves a dangerous window in the system. This patch will mark it dirty if the mod time
|
||
; has been changed across the CMxxxxCN calls. We rely on 2 things:
|
||
; 1. All CMxxxxCN calls set up A2 = VCB, and
|
||
; 2. These calls are single threaded.
|
||
|
||
@NotAfterCMGetCNinRename
|
||
move.l (sp)+, a4 ; grab the caller's address
|
||
|
||
; This patch was rolled into CMSVCS.a <SM1>
|
||
;; patch to CMCreateCN and CMUpdateCN to mark VCB dirty if VCBModTime has been changed:
|
||
cmpROM AfterCMSetupInCMCreateCN,A4 ; was it ROM CMCreateCN? <10Jan91 #27>
|
||
BNE.S @NotCreate ; not create <10Jan91 #27>
|
||
MOVE.L A4,-(sp) ; put the original caller back <10Jan91 #27>
|
||
;; we need to rearrange the A6 stack so it will come back to me after CMCreateCN <10Jan91 #27>
|
||
MOVEM.L (A6)+,D3-D7/A0-A4 ; restore regs that CMCreateCN saves <10Jan91 #27>
|
||
MOVE.L A4,-(SP) ; free A4 so I can use it <10Jan91 #27>
|
||
MOVE.L VCBLsMod(A2),-(A6) ; Save the mod time before the call <10Jan91 #27>
|
||
LEA CMCreateUpdateCNPatch,A4 ; get my patch address <10Jan91 #27>
|
||
MOVE.L A4,-(A6) ; push it on A6 stack <10Jan91 #27>
|
||
MOVEA.L (SP)+,A4 ; restore A4 <10Jan91 #27>
|
||
MOVEM.L D3-D7/A0-A4,-(A6) ; save regs now <10Jan91 #27>
|
||
; the A6 stack now looks like this ... (A7 points to the ret address of the caller of CMSetUp)
|
||
;; ------------- <--- A6 <10Jan91 #27>
|
||
;; D3-D7/A0-A4 <10Jan91 #27>
|
||
;; my patch address <10Jan91 #27>
|
||
;; saved VCBModTime <10Jan91 #27>
|
||
;; original return address <10Jan91 #27>
|
||
|
||
jmpOld ; do the CMSetUP and return to ROM <10Jan91 #27>
|
||
@NotCreate:
|
||
|
||
; This patch was rolled into CMSVCS.a <SM1>
|
||
cmpROM AfterCMSetupInCMUpdateCN,A4 ; was it ROM CMUpdateCN? <10Jan91 #27>
|
||
BNE.S @NotUpdate ; not update <10Jan91 #27>
|
||
MOVE.L A4,-(sp) ; put the original caller back <10Jan91 #27>
|
||
;; we need to rearrange the A6 stack so it will come back to me after CMUpdateCN <10Jan91 #27>
|
||
MOVEM.L (A6)+,D1-D2/A0-A1 ; restore regs that CMCreateCN saves <10Jan91 #27>
|
||
MOVE.L A4,-(SP) ; free A4 so I can use it <10Jan91 #27>
|
||
MOVE.L VCBLsMod(A2),-(A6) ; Save the mod time before the call <10Jan91 #27>
|
||
LEA CMCreateUpdateCNPatch,A4 ; get my patch address <10Jan91 #27>
|
||
MOVE.L A4,-(A6) ; push it on A6 stack <10Jan91 #27>
|
||
MOVEA.L (SP)+,A4 ; restore A4 <10Jan91 #27>
|
||
MOVEM.L D1-D2/A0-A1,-(A6) ; save regs now <10Jan91 #27>
|
||
jmpOld ; do the CMSetUP and return to ROM <10Jan91 #27>
|
||
@NotUpdate:
|
||
|
||
; This patch was rolled into CMSVCS.a <SM1>
|
||
; Replace CMDeleteCN with a fileID-aware version
|
||
cmpROM AfterCMSetupInCMDeleteCN,a4 ; was it ROM CMDeleteCN?
|
||
bne.s NotDelete ; not delete
|
||
jsrOld
|
||
jmp CMDeleteCNAfterSetup ; replace it with the new delete
|
||
NotDelete:
|
||
|
||
; This patch was rolled into CMSVCS.a <SM1>
|
||
; Replace CMMoveCN with a fileID-aware version
|
||
cmpROM AfterCMSetupInCMMoveCN,a4 ; was it ROM CMMoveCN?
|
||
bne.s NotMove
|
||
jsrOld
|
||
jmp CMMoveCNAfterSetup ; replace it with the new move
|
||
NotMove:
|
||
|
||
; The functionality of this patch was rolled into Rename in TFSDIR2.1
|
||
; Replace CMRenameCM with a fileID-aware version that does correct root renames
|
||
cmpROM AfterCMSetupInCMRenameCN,a4 ; was it ROM CMRenameCN?
|
||
bne.s NotRename
|
||
jsrOld
|
||
|
||
; Check for calls to CMRenameCN from PBRename. If we're from PBRename we'll
|
||
; do a quick check for root renames > 27 characters before continuing.
|
||
; Target return address is under CMVars and 10 regs.
|
||
cmpROM AfterCMRenameCNInRename,lenCMVars+(10*4)(a6) ; check if Rename called CMRenameCN
|
||
beq.s RenameCheckVolNameLength
|
||
jmp CMRenameCNAfterSetup ; replace it with the new rename
|
||
NotRename:
|
||
|
||
|
||
; It turns out that the following fix wasn't necessary. A bug had reported that
|
||
; DirCreate and Create could be confused into creating a file or directory in the
|
||
; root when the indicated dirID didn't exist. After fixing the bug I wasn't able
|
||
; to find a case that showed the bug, so I've turned the fix off. If anyone can
|
||
; actually reproduce this, feel free to turn this fix back on. It does do what it
|
||
; says it does. Until then, the dead code stipper will remove all traces of
|
||
; FndFilNameCheckDirValid.
|
||
PerformFndFilNameFix equ 0
|
||
|
||
if PerformFndFilNameFix then
|
||
; This patch was not rolled into SuperMario <SM1>
|
||
; Fix FndFilName to differentiate between #dirNFErr and #fnfErr on pathname
|
||
; parsing. Do a two-stage trigger on calls to CMSetUp from CMGetCN from FndFilName.
|
||
; Target return address is under 4 registers.
|
||
cmpROM AfterCMSetupInCMGetCN, a4 ; was it ROM CMGetCN?
|
||
bne.s NotGetCN
|
||
cmpROM AfterCMGetCNInFndFilName,lenCMVars+(4*4)(a6)
|
||
bne.s NotGetCN
|
||
|
||
import FndFilNameCheckDirValid
|
||
jsrOld ; do the CMSetUp
|
||
jmp FndFilNameCheckDirValid ; replace CMGetCN
|
||
NotGetCN:
|
||
endif
|
||
|
||
; None of the patches triggered, so restore the stack and let the old code
|
||
; do the CMSetUp.
|
||
move.l a4,-(sp) ; put the original caller back
|
||
jmpOld
|
||
|
||
;________________________________________________________________________________
|
||
; Check for renames of the root directory to names > 27 characters in length.
|
||
; We rejoin the fileID-aware version of CMRenameCN after performing our check.
|
||
;
|
||
; Registers:
|
||
; Input: A2.L - VCB pointer
|
||
; D0.L - DirID or parent DirID
|
||
; A0.L - CName pointer (or nil for zero length)
|
||
; A1.L - CName pointer (new CName)
|
||
; D2.L - catalog hint
|
||
;
|
||
; Output: D0.W - result code
|
||
; 0 = ok
|
||
; CMnotfound = CNode not found
|
||
; CMexists = CNode already exists for new CName
|
||
; -n = IO error
|
||
; D2.L - new catalog hint
|
||
;________________________________________________________________________________
|
||
; The functionality of this patch was rolled into Rename in TFSDIR2.1
|
||
RenameCheckVolNameLength:
|
||
cmp.l #FSRtParID,d0 ; Renamed an entry in root's parent [root]?
|
||
beq.s @checkName ; If so, go update vol. name
|
||
cmp.l #FSRtDirID,d0 ; Finally, check for rear entries
|
||
bne.s @noProblem ; If it's not this, we're all set.
|
||
move.l a0, -4(sp) ; test by storing onto live stack
|
||
bne.s @noProblem ; non-zero entries here are not for us
|
||
|
||
@checkName:
|
||
cmp.b #vcbMaxNam, (a1) ; max volume name is 27 characters
|
||
bls.s @noProblem
|
||
|
||
; We’ve caught a rename of the root to a name longer than 27 characters.
|
||
@badName
|
||
moveq.l #bdNamErr,d0 ; bad name result will fall out of Rename
|
||
jmp CMRenameCNExit1
|
||
|
||
@noProblem:
|
||
jmp CMRenameCNAfterSetup ; go continue
|
||
EndProc
|
||
|
||
;________________________________________________________________________________
|
||
; Fix FndFilName <29>
|
||
;
|
||
; When FndFilName is parsing a pathname it changes #cmNotFound errors from
|
||
; CMGetCN into #fnfErr when #dirNFErr is correct. FndFilName returns #dirNFErr
|
||
; when any CMGetCN fails while parsing along a path, and #fnfErr when checking
|
||
; the last leaf in the path. The bug is that FndFilName sees dirID+single segment
|
||
; pathname inputs as the “last segment” case, and returns #fnfErr if the lookup fails,
|
||
; even though the dirID could be invalid.
|
||
;
|
||
; The solution is to do an extra lookup on failed CMGetCN calls while parsing
|
||
; pathnames. We look for the directory thread of the parent of the failed call
|
||
; and set D6 (FndFilName's error register) to #dirNFErr if the second call fails.
|
||
;
|
||
; There may be apps out there which count on #fnfErr to happen in certain cases,
|
||
; so we only trigger on DirCreate and Create, where incorrect handling of this
|
||
; error causes these calls to create file system objects in the wrong directory.
|
||
;
|
||
;
|
||
; Input: A2.L - VCB pointer
|
||
; D0.L - DirID or parent DirID
|
||
; A0.L - CName pointer
|
||
; D2.L - catalog hint
|
||
;
|
||
; Output: D0.W - result code
|
||
; 0 = ok
|
||
; CMnotfound = CNode not found
|
||
; -n = IO error
|
||
; A0.L - pointer to catalog key record (ckr)
|
||
; A1.L - pointer to catalog data record (cdr)
|
||
; D2.L - catalog hint
|
||
;________________________________________________________________________________
|
||
; This patch was not rolled into SuperMario <SM1>
|
||
FndFilNameCheckDirValid: proc
|
||
|
||
createTrapWord equ $a008
|
||
dirCreateTrapWord equ selectDirCreate
|
||
|
||
move.l d0, -(a6) ; stash target parent directory
|
||
|
||
movea.l FSQHead,a1 ; get frontmost file system call
|
||
move.w ioTrap(a1),d0 ; get trap word
|
||
andi.w #$f0ff,d0 ; clear modifiers
|
||
cmpi.w #createTrapWord,d0 ; is this a create call?
|
||
beq.s @trigger
|
||
cmpi.w #dirCreateTrapWord,d0 ; is this a DirCreate call?
|
||
beq.s @trigger
|
||
|
||
move.l (a6)+, d0 ; restore d0
|
||
rts
|
||
|
||
@trigger:
|
||
clr.l vcbDirIDM(A2) ; invalidate current DirID marker
|
||
|
||
jsrROM romLocCNode ; do FndFilName's requested lookup
|
||
bne.s @checkParent
|
||
|
||
addq.w #4,a6 ; restore a6 stack level
|
||
bra.s @exit ; and return to a happy FndFilName
|
||
|
||
@checkParent:
|
||
suba.l a0, a0 ; go after the parent
|
||
move.l (a6)+,d0 ; retrieve target parent directory
|
||
|
||
jsrROM LOCCREC
|
||
bne.s @dirNotFound
|
||
cmpi.b #cdrThdRec,cdrType(a1) ; is it a directory thread?
|
||
beq.s @fileNotFound ; yes, so it was just the leaf that was missing
|
||
@dirNotFound:
|
||
moveq.l #dirNFErr,d6 ; stuff correct error code in FndFilName's register
|
||
@fileNotFound:
|
||
move.w #cmNotFound,d0 ; just like they expected
|
||
@exit:
|
||
add.w #lenCMVars,a6 ; de-allocate memory for CM vars
|
||
movem.l (a6)+,d1/d3/a3-a4 ; restore regs
|
||
move.l (a6)+,-(sp) ; put return address back on stack
|
||
tst.w d0 ; set condition codes
|
||
rts ; exit CMGetCN back to FndFilName
|
||
|
||
;_____________________________________________________________________________________
|
||
; <38>
|
||
; CountFCBs
|
||
;
|
||
; Function:
|
||
; Return the number of free FCBs
|
||
; Return the total number of FCBs
|
||
;
|
||
; Use d0 as a max number to count, to allow for callers to check for a
|
||
; specific quantity of free FCBs.
|
||
;
|
||
; Input:
|
||
; d0.w - requested # of free FCBs
|
||
;
|
||
; Output:
|
||
; d0.w - number of free FCBs (clipped to input value of d0)
|
||
; d1.w - total number of FCBs
|
||
;
|
||
; ccr - Z set if requested number were free
|
||
; Z clear if not
|
||
;
|
||
; Note: When called with d0.w = #-1, the output in d1 is the total number
|
||
; of FCBs that exist.
|
||
;
|
||
;_____________________________________________________________________________________
|
||
; This routine has been copied to TFS.a <SM1>
|
||
CountFCBs proc
|
||
@Regs reg d2/a0/a1
|
||
|
||
movem.l @Regs,-(sp)
|
||
move.l FCBSPtr,a1 ; point to base of FCB array
|
||
moveq.l #0,d1 ; clear high word
|
||
move.w (a1),d1 ; grab length of the array
|
||
lea.l 2(a1),a0 ; point to base of 1st FCB
|
||
add.w d1,a1 ; point to end of FCB array
|
||
divu FSFCBLen,d1 ; d1 = number of FCBs
|
||
move.w d0,d2 ; copy limit value
|
||
|
||
@loop:
|
||
suba.w FSFCBLen,a1 ; point to beginning of previous FCB
|
||
tst.l (a1) ; is it free?
|
||
bne.s @notFree
|
||
subq.w #1,d2 ; tally the free FCB
|
||
beq.s @enough ; hit our quota?
|
||
@notFree:
|
||
cmpa.l a0,a1 ; are we done with the array?
|
||
bhi.s @loop ; as long as we're above the bottom now
|
||
@enough:
|
||
sub.w d2,d0 ; d0 = number of free FCBs
|
||
tst.w d2 ; d2 is zero if we hit our target
|
||
movem.l (sp)+,@Regs
|
||
rts
|
||
endproc
|
||
|
||
;_____________________________________________________________________________________
|
||
; <38>
|
||
; OpenAttemptHook
|
||
;
|
||
; Function:
|
||
; This procedure is called each time some sort of Open call (one that allocates
|
||
; an FCB) is attempted. We're here to schedule the FCB allocator if
|
||
; necessary. The FCB allocator is now run after the sync wait loop (on failed
|
||
; opens) and at _SynchIdleTime, although it could be run anytime moving memory
|
||
; is cool.
|
||
;
|
||
; Optimization:
|
||
; Since counting the number of free FCBs is time consuming, we use fsFCBCounter
|
||
; to hold the approximate number of free FCBs in excess of the fsFCBBurst amount.
|
||
; We conservatively assume that each time we attempt an open we use up an FCB. This
|
||
; is obviously wrong, since some opens fail and _Close frees up FCBs, but this
|
||
; will allow us to avoid our more expensive check most of the time. When the counter
|
||
; does hit zero we count the array and set fsFCBCounter back up. We also might set
|
||
; the fsNeedsFCBs value if there are too few FCBs around.
|
||
;
|
||
; Input:
|
||
; a0 - pointer to caller's PB
|
||
;
|
||
; Output:
|
||
; none
|
||
;_____________________________________________________________________________________
|
||
; This routine has been copied to TFS.a <SM1>
|
||
OpenAttemptHook: proc
|
||
@regs reg d0/d1/a1
|
||
movem.l @regs,-(sp)
|
||
|
||
move.l FSVarsPtr,a1
|
||
with FSVars
|
||
|
||
btst.b #fsNoFCBExpansion,fsFlags(a1) ; are we in the ballgame?
|
||
bne.s @exit
|
||
|
||
sub.w #1,fsFCBCounter(a1) ; see if we need to check
|
||
bgt.s @exit ; no need to check if we're > 0
|
||
|
||
moveq.l #-1,d0 ; we want to know about all FCBs
|
||
bsr CountFCBs ; see how many there are
|
||
|
||
sub.w fsFCBBurst(a1),d0 ; do we have enough headroom?
|
||
bls.s @needMore ; need more if (#free) <= (burst amount)
|
||
|
||
move.w d0,fsFCBCounter(a1) ; remember the amount of slack
|
||
bra.s @exit
|
||
|
||
@needMore:
|
||
bset.b #fsNeedFCBs,fsFlags(a1) ; remind ourselves of our need
|
||
clr.w fsFCBCounter(a1) ; and make sure we check next time
|
||
|
||
@exit:
|
||
movem.l (sp)+,@regs
|
||
rts
|
||
|
||
endproc
|
||
|
||
;_____________________________________________________________________________________
|
||
; <38>
|
||
; PreflightFileOpen
|
||
;
|
||
; Function:
|
||
; Call OpenAttemptHook for calls to _Open/_HOpen/_OpenDF/_HOpenDF
|
||
;
|
||
; Input:
|
||
; a0 - pointer to caller's PB
|
||
;
|
||
; This function sits at the bottom of the jFileOpen vector
|
||
;_____________________________________________________________________________________
|
||
; This patch was rolled into TFSDIR1.a
|
||
PreflightFileOpen proc
|
||
|
||
jsrROM FSQUEUE ; serialize things
|
||
bsr OpenAttemptHook ; tell'em we're trying
|
||
ST RegRsrc ; open regular part of file <39>
|
||
jmpROM FOpen1
|
||
|
||
endproc
|
||
|
||
;_____________________________________________________________________________________
|
||
; <38>
|
||
; PreflightOpenRF
|
||
;
|
||
; Function:
|
||
; Call OpenAttemptHook for calls to _OpenRF/_HOpenRF
|
||
;
|
||
; Input:
|
||
; a0 - pointer to caller's PB
|
||
;
|
||
;_____________________________________________________________________________________
|
||
; This patch was rolled into TFSDIR1.a
|
||
PreflightOpenRF PatchProc _OpenRF
|
||
|
||
jsrROM FSQUEUE ; serialize things
|
||
bsr OpenAttemptHook ; tell'em we're trying
|
||
clr.b RegRsrc ; open resource part of file <39>
|
||
jmpROM FOpen1
|
||
|
||
endproc
|
||
|
||
;_____________________________________________________________________________________
|
||
; <38>
|
||
; AllocateFCBs
|
||
;
|
||
; Function:
|
||
; This is a private queued file system call which returns information
|
||
; about the FCB array and can allocate more FCBs.
|
||
;
|
||
; If a request for more FCBs is being made then AllocateFCBs must be
|
||
; called only when moving memory is legal. If the call is made for
|
||
; information only it can be made anytime.
|
||
;
|
||
; parameter block:
|
||
; ioFCBBurst offset $20, word, input requested burst headroom
|
||
; ioFCBGrow offset $22 word, input requested free if allocation triggered
|
||
; ioTotalFCBs offset $24 word, output returns number of FCBs present
|
||
; ioTotalFreeFCBs offset $26 word, output returns number of free FCBs
|
||
; ioFCBsAdded offset $2A, word, output returns number allocated this time
|
||
;
|
||
; Input:
|
||
; a0.l - pointer to PB
|
||
;
|
||
; Output:
|
||
; d0.w - #noErr if at least one FCB is free on exit.
|
||
; - #tmfoErr if no FCBs are free on exit
|
||
;
|
||
; AllocateFCBs always counts the total number of FCBs and the total number free. If there
|
||
; are ioFCBBurst FCBs free, no other work is performed. If there are fewer than ioFCBBurst
|
||
; FCBs free then AllocateFCBs will attempt to allocate enough new ones so that ioFCBGrow
|
||
; are free.
|
||
;
|
||
; ioFCBBurst and ioFCBGrow work together. ioFCBBurst is the number of opens that we'd
|
||
; like to be able to perform between visits to sync time. ioFCBGrow is the number that
|
||
; we'd like to have available when we go to the trouble of growing the array. ioFCBBurst
|
||
; must always be <= ioFCBGrow.
|
||
;
|
||
;_____________________________________________________________________________________
|
||
; This routine was rolled into TFS.a
|
||
AllocateFCBs proc
|
||
import MoreFCBs
|
||
|
||
jsrROM FSQUEUE ; wait our turn
|
||
|
||
moveq.l #-1,d0 ; more FCBs than we'll ever have
|
||
bsr CountFCBs ; count all of them
|
||
move.w d0,ioTotalFreeFCBs(a0) ; remember how many are free
|
||
move.w d1,ioTotalFCBs(a0) ; remember how many there are
|
||
|
||
movea.l FSVarsPtr,a1
|
||
with FSVars
|
||
move.w ioFCBBurst(a0),d2 ; get the # we'd like to keep free
|
||
cmp.w d2,d0 ; see if we have that many free
|
||
bhs.s @noAllocate
|
||
|
||
move.w ioFCBGrow(a0),d2 ; d2 = the number of free FCBs we'd like to grow to
|
||
sub.w d0,d2 ; d2 = the number we need to grow to there
|
||
|
||
move.w fsFCBMax(a1),d3 ; d3 = the most FCBs we can have
|
||
sub.w d1,d3 ; d3 = the most we can grow
|
||
beq.s @noAllocate ; bail if we're out of array room
|
||
cmp.w d2,d3 ; can we grow as much as we want to?
|
||
bhs.s @grow ; if (most we can grow) >= (amount desired), we're OK
|
||
move.w d3,d2 ; otherwise, just grow as much as we can
|
||
|
||
@grow:
|
||
bsr MoreFCBs ; try to get the FCBs
|
||
beq.s @gotSome ; leave with a smile
|
||
|
||
asr.w #1,d2 ; try to get half that many
|
||
beq.s @noAllocate ; if we're down to zero, bail
|
||
bra.s @grow
|
||
|
||
@gotSome:
|
||
add.w d2,ioTotalFCBs(a0) ; update the total count
|
||
add.w d2,ioTotalFreeFCBs(a0) ; update the free count
|
||
move.w d2,ioFCBsAdded(a0) ; note the number added
|
||
bra.s @done
|
||
|
||
@noAllocate:
|
||
clr.w ioFCBsAdded(a0) ; we didn't add any new ones
|
||
|
||
@done:
|
||
tst.w ioTotalFreeFCBs(a0) ; check the number that are free now
|
||
beq.s @noFCBsAvailable ; if none are left, report the problem
|
||
moveq.l #noErr,d0 ; if some are left, we're happy
|
||
|
||
@exit: jmpROM CMDDONE
|
||
|
||
@noFCBsAvailable:
|
||
moveq.l #tmfoErr,d0
|
||
bra.s @exit
|
||
endwith
|
||
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
|
||
;_____________________________________________________________________________________
|
||
; This routine was rolled into TFS.a
|
||
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
|
||
|
||
;_____________________________________________________________________________________
|
||
; <38>
|
||
; 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
|
||
;_____________________________________________________________________________________
|
||
; This routine was rolled into TFS.a
|
||
FileManagerIdleProc: proc export
|
||
jsr CheckFCBAllocation
|
||
rts
|
||
endproc
|
||
|
||
;________________________________________________________________________________
|
||
; Fix DtrmV3
|
||
;
|
||
; The ROM's volume identification code at DtrmV3 forgets to clear d2
|
||
; (path string length) when there's a null string (i.e. length byte is zero).
|
||
;
|
||
; This patch replaces DtrmV3 entirely (all five lines). Also, though the ROM DtrmV3
|
||
; branches to DtrmVol, it does this just to put the name pointer in d2, which we've
|
||
; already done, so we jump straight through the vector at DtrmV2.
|
||
;
|
||
; NOTE: We only fix this bug on machines where there's a convenient vector on jDtrmV3.
|
||
; This bug has been around for a long time and hasn't cause too much trouble, so we
|
||
; can live with this partial solution.
|
||
;________________________________________________________________________________
|
||
; This patch was rolled into TFSVOL.a
|
||
FixDtrmV3: PatchProc jDtrmV3,(II,IIci,Portable)
|
||
|
||
move.l ioFileName(a0),d2 ; entry for flush,set,eject,unmountvol
|
||
move.l d2,a4
|
||
beq.s @DtrmV1 ; br if nil filename pointer (d2,a4=0)
|
||
tst.b (a4) ; is it a zero-length name?
|
||
beq.s @SetStringLengthZero ; if zero, set the string length to zero
|
||
; and fall through to DtrmVol
|
||
|
||
; Jumping straight to DtrmV2 is the moral equivalent of the ROM's jump to
|
||
; DtrmVol since all DtrmVol does is move the pathname pointer into d2 and fall
|
||
; through to DtrmV2.
|
||
move.l jDtrmV2, -(sp) ; let the ROM take it from here
|
||
rts
|
||
|
||
@SetStringLengthZero:
|
||
moveq.l #0, d2 ; make sure string length is set to zero
|
||
|
||
@DtrmV1:
|
||
jmpROM romDtrmV1 ; let the ROM take it from here
|
||
endproc
|
||
|
||
;_______________________________________________________________________
|
||
; <29>
|
||
; Fix _MountVol to
|
||
; not allocate any memory during disk switch operations.
|
||
; not attempt to _Offline other volumes to free up memory
|
||
; not reopen btree files on remounts
|
||
;
|
||
; In order to make the disk switch hook run without moving memory, we need
|
||
; to be able to check for a remount without moving memory. If we find a
|
||
; remount, we succeed. If we see the #fsNoAllocate flag, we'll return an
|
||
; error, causing the disk switch code to eject the disk and wait for another.
|
||
; Otherwise we jump into the ROM and let it do a new mount.
|
||
;
|
||
; When we do see a remount, we tweak the driver refNum and drive number and
|
||
; we're done, since the btree files will always be open.
|
||
;
|
||
; The flag #fsNoAllocate will be set by the disk switch hook to inform the
|
||
; file system that it should allow only remounts (i.e. not allocate any memory).
|
||
; If new mounts are allowed, we jump straight into the ROM code which mounts the
|
||
; volume, skipping the old code which attempted to _Offline other mounted
|
||
; volumes in favor of a newly mounted one.
|
||
;
|
||
; Exciting note for you wanna-be assemblers out there who write instructions
|
||
; into lomem at TrapAgain and jump to them. You know who you are (Disk Switch
|
||
; Hook). You might have been worried about flushing those whizzy processor
|
||
; caches that some of our more profitable macintoshes sport these days. However,
|
||
; as an added introductory bonus, successful mountvol calls flush the caches
|
||
; because they call _BlockMove when they fill in the fresh VCB from the MDB.
|
||
; _BlockMove is just one of those cache-flushing kind of guys.
|
||
;_______________________________________________________________________
|
||
; This patch was rolled into TFSVOL.a
|
||
MountVolFor1991 PatchProc _MountVol
|
||
|
||
jsrROM FSQUEUESYNC
|
||
|
||
movea.l a0, a5 ; save caller's pb
|
||
move.w ioDrvNum(a5), d2 ; where FindDrive likes it
|
||
jsrROM romFindDrive ; call ROM (not vectored on Plus/SE <30>)
|
||
bne EarlyExit
|
||
|
||
suba.w #512,a6 ; a disk block
|
||
|
||
lea.l Params,a0 ; the old lomem param block
|
||
move.w d2,ioDrvNum(a0) ; drive number
|
||
move.w d1,ioRefNum(a0) ; driver RefNum
|
||
move.l a6,ioBuffer(a0) ; buffer
|
||
move.w #fsFromStart,ioPosMode(a0) ; position mode 1 (from disk start)
|
||
move.l #1024, ioPosOffset(a0) ; The MDB is always block #2
|
||
move.l #512,ioByteCount(a0) ; All MDBs are 1 512 byte block
|
||
_Read
|
||
bne Exit
|
||
|
||
moveq.l #NoMacDskErr,d0 ; Assume it's for another file system.
|
||
cmp.w #tSigWord,drSigWord(a6) ; Does it bear the Turbo Seal?
|
||
beq.s @GotMacDisk
|
||
|
||
cmp.w #sigWord,drSigWord(a6) ; Is it a trustworthy MFS volume?
|
||
bne Exit
|
||
@GotMacDisk:
|
||
|
||
moveq.l #badMDBErr,d0 ; in case master directory block is bad
|
||
move.l drAlBlkSiz(a6),d3 ; make sure this is non-zero, 512 multiple
|
||
beq Exit ; exit if 0
|
||
andi.w #$01FF,d3 ; 512-byte multiple?
|
||
bne Exit ; exit if not
|
||
|
||
move.w #notARemountErr,d0 ; 'cause we're looking for remounts only here
|
||
move.l VCBQHdr+qHead,d3 ; search the queue of VCBs
|
||
|
||
CheckRemountLoop:
|
||
beq.s Exit ; we fail if we don't see a remount
|
||
move.l d3,a2
|
||
|
||
tst.w vcbDrvNum(a2) ; matching volume better be off-line (0 drive num)
|
||
bne.s @Next
|
||
|
||
move.w vcbSigWord(a2),d3 ; Pick up Sigword of mounted volume
|
||
cmp.w drSigWord(a6),d3 ; Same signature?
|
||
bne.s @Next
|
||
move.l vcbCrDate(a2),d3 ; Pick up creation date of mounted volume
|
||
cmp.l drCrDate(a6),d3 ; Same create date?
|
||
bne.s @Next
|
||
move.l vcbLsMod(a2),d3 ; Pick up mod date of mounted volume
|
||
cmp.l drLsMod(a6),d3 ; Same mod date?
|
||
bne.s @Next
|
||
|
||
; At this point, we're fairly certain that the two volumes are, in fact, the same,
|
||
; but just to make sure (and avoid problems with stuck clocks), we'll compare the
|
||
; volume names as well:
|
||
|
||
lea.l vcbVN(a2),a1 ; Point to name of mounted volume
|
||
lea.l drVN(a6),a3 ; Point to name of current volume
|
||
moveq.l #(vcbMaxNam+1)-1,d3 ; check 28 bytes in name field (incl. length)
|
||
@loop:
|
||
cmpm.b (a1)+,(a3)+ ; field match?
|
||
dbne d3,@loop
|
||
beq.s Remount
|
||
|
||
@Next:
|
||
move.l qLink(a2),d3
|
||
bra.s CheckRemountLoop
|
||
|
||
; We are now convinced that we have a remount. We will rebuild the attributes
|
||
; byte, which is the only one that could have changed. We'll also store the
|
||
; current drive number and driver refNum, and mark the volume dirty.
|
||
Remount:
|
||
move.w #drvStsCode,csCode(a0) ; drive status!
|
||
clr.b 2+csParam(a0) ; Clear status byte for return
|
||
_Status ; refnum and drivenum set up by read call
|
||
|
||
moveq.l #-128,d0 ; $80 mask
|
||
and.b 2+csParam(a0),d0 ; WriteProt status get
|
||
move.b d0,vcbAtrb+1(a2) ; write-protect, zeroed consistency status
|
||
_AssumeEq vcbWrProt,7
|
||
|
||
move.w d2,vcbDrvNum(a2) ; drive number
|
||
move.w d1,vcbDRefNum(a2) ; driver RefNum
|
||
|
||
jsrROM TFSVCBTST ; remounted a TFS volume? <32>
|
||
bne.s NoErrExit ; Nope - don't mess with the MDB <33>
|
||
btst.b #vcbWrProt,vcbAtrb+1(a2); Is volume write protected? <32>
|
||
bne.s NoErrExit ; If so, don't try to flush the MDB <33>
|
||
bclr.b #vcbAtVOK,vcbAtrb(a2) ; From now 'til unmount we're dirty <32>
|
||
jsrROM MarkVCBDirty ; mark VCB dirty so it will be written <32>
|
||
jsrROM FlushMDB ; write it <32>
|
||
NoErrExit:
|
||
moveq.l #noErr,d0
|
||
|
||
Exit:
|
||
adda.w #512, a6 ; deallocate MDB buffer
|
||
|
||
EarlyExit:
|
||
tst.w d0 ; check the error
|
||
beq.s @1 ; noErr implies a successful remount
|
||
|
||
; if we are allowing new mounts, let the ROM handle the details. Otherwise,
|
||
; let the error stand
|
||
move.l FSVarsPtr,a1
|
||
btst.b #fsNoAllocate, FSVars.fsFlags(a1)
|
||
beqROM OldMtVolAfterFSQueue ; join the ROM (BTW, skipping the _Offline heroics)
|
||
@1:
|
||
jmpROM CMDDONE
|
||
endproc
|
||
|
||
;_______________________________________________________________________
|
||
; <30>
|
||
; Fix _Offline and _Eject to leave btree control files open
|
||
;
|
||
; In order to make the disk switch hook run without moving memory, we need
|
||
; to leave the btree control files (catalog and extents) open when putting
|
||
; a volume offline, so that the subsequent _Mount call won't have to allocate
|
||
; a new btree control block.
|
||
;
|
||
; We head patch _Eject and _Offline to use this code path instead of
|
||
; the ROM code.
|
||
;
|
||
; Registers:
|
||
; a0 - iopb
|
||
;
|
||
; This patch handles the cases for the Plus and SE
|
||
;_______________________________________________________________________
|
||
; These patches were rolled into TFSVOL.a
|
||
NoCloseOnOffline PatchProc _Offline
|
||
import OfflineEjectCommon
|
||
jsrROM FSQUEUESYNC ; wait until all current calls are done
|
||
st NoEject ; -1 = send offline but don't eject
|
||
jmp OfflineEjectCommon ; share code with Eject
|
||
endproc
|
||
|
||
NoCloseOnEject PatchProc _Eject
|
||
export OfflineEjectCommon
|
||
jsrROM FSQUEUESYNC ; wait until all current calls are done
|
||
clr.b NoEject ; 0 = offline + eject
|
||
|
||
OfflineEjectCommon
|
||
jsrROM DTRMV3 ; check name, drive number, etc.
|
||
bne.s ejectNotMounted ; br if drive not mounted (why flush?)
|
||
jsrROM romCkExtFS ; see if it's for an external fs
|
||
bne.s EjectDone ; exit if so
|
||
bra.s ejectMounted ; otherwise, go the 'mounted volume' route
|
||
|
||
ejectNotMounted:
|
||
move.w ioVDrvNum(a0),d2 ; drive number
|
||
|
||
ejectDrvNum:
|
||
jsrROM romFindDrive ; get disk driver refnum in D1
|
||
bne.s ejectDone ; exit if no mapping for this drive
|
||
tst.b NoEject ; REALLY eject this thing?
|
||
beqROM EjectIt ; OK, OK; it's going already...
|
||
|
||
ejectDone:
|
||
jmpROM CMDDONE ; we're done . . .
|
||
|
||
ejectMounted:
|
||
lea.l vcbDrvNum(a2),a1 ; point into VCB at drive number
|
||
move.w (a1),d2 ; on-line, non-ejected?
|
||
bne.s ejectOnLine ; br if so
|
||
|
||
move.w 2(a1),d2 ; does dRefNum say it's ejected?
|
||
bpl.s ejectDone ; just exit if so
|
||
tst.b NoEject ; no eject?
|
||
bne.s ejectDone ; br if so (it's already offline . . .)
|
||
|
||
neg.w d2 ; get non-ejected offline disk's drive number
|
||
neg.w 2(a1) ; mark it now ejected . . .
|
||
bra.s ejectDrvNum ; and eject it (pass D2=drive number)
|
||
; NOTE: the previous OffLine call would have marked
|
||
; a TFS disk consistent.
|
||
|
||
; OK, it's on-line and not ejected . . . so make it offline and eject if appropriate . . .
|
||
|
||
ejectOnLine:
|
||
jsrROM romFindDrive ; Find DQE from drive number in d2
|
||
bne.s ejectDone ; Punt if can't be found
|
||
tst.b NoEject ; Just an _Offline call?
|
||
bne.s @5 ; Yes - don't try harder than needed
|
||
cmp.b #8,DQDIP(a3) ; Check drive ejectability
|
||
blt.s @5 ; Skip if it's ejectable
|
||
st NoEject ; If DQDIP > 8, it's non-ejectable
|
||
cmp.b #64,DQDIP(a3) ; Send a _Control call anyway?
|
||
blt.s @5 ; No need - we're set as is
|
||
move.b #1,NoEject ; If >64, call the driver anyway
|
||
|
||
@5: st FlushOnly ; only flushing (don't close . . .)
|
||
jsrROM FlushVFiles ; flush all files on this volume
|
||
|
||
jsrROM TFSVCBTST ; Are we dealing with a TFS volume?
|
||
bneROM OfflineEjectCallsMFSFlush ; Nope, so join ROM to do MFS right
|
||
|
||
; note that the following line is the opposite of the ROM code. We only want to flush
|
||
; these files, since _Unmount will close them later. However, we do want to mark the
|
||
; vcb consistent as it goes offline.
|
||
jsrROM CVFLGS ; Is volume write protected? <31>
|
||
bne.s @1 ; If so, don't try to mark it dirty <31>
|
||
bset.b #vcbAtVOK,vcbAtrb(a2) ; Indicate vol was unmounted ok <31>
|
||
jsrROM MarkVCBDirty ; mark VCB dirty so it will be written <31>
|
||
@1 jsrROM FlushMDB ; Go flush the VCB info BEFORE flushing <31>
|
||
|
||
st.b FlushOnly ; just flush the control files
|
||
jmpROM OfflineEjectCallsFlushBuffers
|
||
endproc
|
||
|
||
;________________________________________________________________________________
|
||
; <36>
|
||
; Routine: DesktopCloseDownProc
|
||
;
|
||
; Inputs param block to _Unmount
|
||
;
|
||
; Outputs: (none)
|
||
;
|
||
; Function: Close the desktop database on _Unmount calls
|
||
;
|
||
;________________________________________________________________________________
|
||
; Rolled into TFS.a for <SM1> FM
|
||
DesktopCloseDownProc: proc
|
||
import FindDTVol
|
||
@regs reg a0/a1/a3/d0/d1 ; <43>
|
||
|
||
movem.l @regs, -(sp)
|
||
|
||
movea.l a0,a3 ; save the user's pb <42>
|
||
|
||
; d0 - pascal length byte of string <43>
|
||
; d1 - size of string allocated on stack <43>
|
||
|
||
moveq.l #0,d1 ; assume no string on stack <43>
|
||
move.l ioNamePtr(a3),d0 ; d0 = caller’s name ptr <43>
|
||
beq.s @noName ; bail on nil <43>
|
||
movea.l d0,a0 ; get ready to copy <43>
|
||
moveq.l #0,d0 ; clear high bytes <43>
|
||
move.b (a0),d0 ; d0 = string length <43>
|
||
beq.s @noName ; bail on zero length (d0.l has nil) <43>
|
||
|
||
move.b d0,d1 ; d1 = copy of string length byte <43>
|
||
addq.b #2,d1 ; add length byte and rounding fodder <43>
|
||
bclr.l #0,d1 ; make it even <43>
|
||
suba.w d1,sp ; allocate a string of the right length <43>
|
||
movea.l sp,a1 ; point to it <43>
|
||
|
||
@1: move.b (a0)+,(a1)+ ; <43>
|
||
dbra d0,@1 ; copy string length+1 bytes <43>
|
||
|
||
move.l sp,d0 ; point to it again <43>
|
||
|
||
@noName:
|
||
suba.w #ioHVQElSize,sp ; get a pb <42>
|
||
movea.l sp,a0 ; point to it <42>
|
||
move.w ioVRefNum(a3),ioVRefNum(a0) ; copy caller's vRefNum <42>
|
||
move.l d0,ioNamePtr(a0) ; our version of the caller's name <42>
|
||
move.w #-1,ioVolIndex(a0) ; by name&vRef, please <42>
|
||
_GetVolInfo ; <42>
|
||
bne.s @done ; <42>
|
||
|
||
move.w ioVRefNum(a0), d0 ; grab the volume that's going away
|
||
bsr FindDTVol ; try to find a DTDBQElt for this volume
|
||
bne.s @done ; no work to do if the DTDB is closed
|
||
sub.w #ioDTQElSize, sp ; allocate a DT param block
|
||
movea.l sp, a0
|
||
move.w DTDBQElt.DTRefNum(a3), ioRefNum(a0) ; stash the DTRefNum for this volume
|
||
_DTCloseDown
|
||
add.w #ioDTQElSize, sp ; deallocate the param block
|
||
|
||
@done:
|
||
adda.w #ioHVQElSize,sp ; deallocate the pb <42>
|
||
adda.w d1,sp ; deallocate the string <43>
|
||
movem.l (sp)+, @regs
|
||
rts
|
||
endproc
|
||
|
||
;_______________________________________________________________________
|
||
;
|
||
; Routine: UnMountVol
|
||
; Arguments: A0 (input) -- pointer to volume parameter block, uses IODrvNum,
|
||
; IOFileName
|
||
; D0 (output) -- error code
|
||
; This call is executed synchronously.
|
||
; Calls: FlushVolume
|
||
;
|
||
; Function: All files on the volume in the drive are closed and any changed
|
||
; directory information is written out to the diskette. Memory
|
||
; for the VCB, volume buffer, and block map is deallocated.
|
||
;
|
||
; This is a complementary patch to the above patch to _Offline. Since
|
||
; _Offline does not close the HFS control files (catalog and extents)
|
||
; we need to have _UnmountVol always close them.
|
||
;_______________________________________________________________________
|
||
UnmountForTheNineties: PatchProc _UnMountVol
|
||
|
||
bsr DesktopCloseDownProc ; go take care of the desktop database <36>
|
||
|
||
; The desktop file hack should no longer be necessary for the 7.0 version of the AppleShare client.
|
||
IncludeAppleShareDesktopHack equ 0
|
||
|
||
jsrROM FSQUEUESYNC ; Get in sync...
|
||
clr.b FlushOnly ; Setup same as UnmountVol
|
||
jsrROM DTRMV3 ; Call DtrmV3 to do setup stuff
|
||
bneROM CMDDONE ; and split on errors
|
||
|
||
moveq #0,d0 ; Initialize result code
|
||
btst #HFSBit,ioTrap(a0) ; Unconditional unmount?
|
||
bne FlUnMnt ; Xfer if so...
|
||
;
|
||
; On return from DtrmV3, A2 contains the VCB ptr of the volume
|
||
; in question. Search the FCB array for open files that reference
|
||
; the volume...
|
||
;
|
||
movem.l a1/d1/d2,-(sp) ; Save some scratch regs
|
||
move.l FCBsPtr,a1 ; FCB array base address
|
||
moveq #2,d1 ; Index of 1st FCB
|
||
@2
|
||
move.l fcbFlNm(a1,d1),d2 ; Is the file currently open?
|
||
beq.s @4 ; Nope, try next FCB...
|
||
cmp.l fcbVPtr(a1,d1),a2 ; Is the file on this volume?
|
||
bne.s @4 ; No, try next FCB...
|
||
cmp.w #SigWord,vcbSigWord(a2) ; Is this an MFS volume?
|
||
beq.s @3 ; Yes, error if files are open...
|
||
cmp.l #FSUsrCNID,d2 ; Is it an internal file?
|
||
blo.s @4 ; Ignore it if so...
|
||
if IncludeAppleShareDesktopHack then
|
||
cmp.w #Tsigword,vcbSigWord(a2) ; Is this a TFS volume? ••• do we mean extFS?
|
||
bne.s @3 ; Error if not...
|
||
cmp.l #$47525420,d2 ; is it a fake AppleShare FCB?
|
||
beq.s @4 ; yes, skip it...
|
||
endif
|
||
|
||
@3
|
||
;
|
||
; Found an open user file on the volume, so return a
|
||
; busy condition...
|
||
;
|
||
moveq #fBsyErr,d0 ; Assert UnmountVol error
|
||
bra.s @5 ; Get out...
|
||
|
||
@4
|
||
add.w FSFCBLen,d1 ; Next FCB array entry
|
||
cmp.w (a1),d1 ; Reached the end yet?
|
||
blo.s @2 ; Continue if not...
|
||
@5
|
||
movem.l (sp)+,a1/d1/d2 ; Restore regs
|
||
tst.w d0 ; Were files open?
|
||
bneROM CMDDONE ; and quit on errors
|
||
|
||
FlUnMnt:
|
||
jsrROM romCkExtFS ; see if it's for an external fs
|
||
bneROM CMDDONE ; and split on errors
|
||
|
||
jsrROM FlushVFiles ; flush all files on this volume
|
||
bneROM CMDDONE ; and split on errors
|
||
|
||
; All files on this volume are flushed now. Update the volume information as
|
||
; appropriate, depending on the file structure:
|
||
|
||
jsrROM TFSVCBTST ; is this a TFS volume?
|
||
beq.s @1 ; br if so
|
||
jmpROM FlUnMntAfterMFSCheck ; the ROM has is right for MFS volumes
|
||
|
||
; Unmounting a TFS volume: close the volume control B*-Tree and use the
|
||
; volume buffer to write out the MDB
|
||
;
|
||
; <30>
|
||
; Here's the change: we no longer check whether the volume is offline. Instead we always
|
||
; go ahead and close the control files, since we no longer close them on _Offline or _Eject.
|
||
|
||
@1: tst.w vcbDrvNum(a2) ; Check drive number: vol. offline?
|
||
beq.s CloseControlFiles ; If zero, it is; control files are open & flushed
|
||
jmpROM FlUnMntCallsFlushBuffers ; otherwise, rejoin the ROM path which does all
|
||
|
||
; here begins CloseControlFiles, which is derived from FlushBuffers
|
||
CloseControlFiles:
|
||
clr.b FlushOnly ; close the control files
|
||
movea.l FCBsPtr,A1 ; Point to FCB array
|
||
|
||
move.w vcbCTRef(a2),d1 ; Catalog B*-Tree file refnum
|
||
jsrROM FClose ; Close the file itself
|
||
bne.s FlVolExit ; Punt on errors
|
||
|
||
move.w vcbXTRef(a2),d1 ; Extent B*-Tree file refnum
|
||
jsrROM FClose ; Close the file itself
|
||
bne.s FlVolExit ; Rats - and we were SO CLOSE.. •• is this what we want?
|
||
|
||
jsrROM DsposVBlks ; Trash blocks for a2 volume
|
||
|
||
; here ends CloseControlFiles
|
||
|
||
BackInFlUnMnt:
|
||
jsrROM DsposVCB ; share code with MountVol error routine
|
||
|
||
moveq.l #noErr,d0 ; no error
|
||
|
||
FlVolExit
|
||
jmpROM CMDDONE
|
||
endproc
|
||
|
||
|
||
;__________________________________________________________________________________
|
||
; <29>
|
||
; Routine: Fix BTFlush
|
||
;
|
||
; Function: The ROM checks to make sure that the btree file it is about to
|
||
; to flush isn't on an offline volume before going and asking for
|
||
; the header block. Unfortunately, it forgets to clear d0, so
|
||
; the caller sees an error and fails.
|
||
;
|
||
; This abbreviated version of BTFlush kicks in when we see an
|
||
; offline volume.
|
||
;
|
||
; Input: D0.W - file refnum
|
||
;
|
||
; Output: D0.W - result code
|
||
; 0 = ok
|
||
; -n = IO error
|
||
;__________________________________________________________________________________
|
||
; Rolled into BTSvcs.a for SuperMario <SM1> FM
|
||
|
||
FixBTFlush PatchProc jBTFlush
|
||
|
||
move.l (sp)+,-(a6) ; save return address on A6 stack
|
||
movem.l d1-d4/a0-a4,-(a6) ; save regs
|
||
move.w d0,d3 ; d3 = file refnum
|
||
movea.l FCBsPtr,a1 ; point into FCB array
|
||
movea.l FCBVPtr(a1,d3.w),a2 ; point to VCB
|
||
|
||
tst.w VCBDrvNum(a2) ; volume offline?
|
||
beq.s BFExit ; yes, nothing could have changed ->
|
||
|
||
movem.l (a6)+,d1-d4/a0-a4 ; restore regs
|
||
move.l (a6)+,-(sp) ; put return address back on stack
|
||
jmpOld ; do a real flush
|
||
|
||
BFExit:
|
||
movem.l (a6)+,d1-d4/a0-a4 ; restore regs
|
||
move.l (a6)+,-(sp) ; put return address back on stack
|
||
moveq.l #0, d0 ; indicate success
|
||
rts ; exit BTFlush
|
||
endproc
|
||
|
||
|
||
;________________________________________________________________________________
|
||
; Fix the Disk Switch handler to
|
||
; never attempt to save the bits behind the disk switch alert.
|
||
; notify _Mount that it can't allocate any memory
|
||
;
|
||
; With color quickdraw, calling _InitPort or _CopyBits might move memory,
|
||
; something that the disk switch hook isn't supposed to do. Also, _MountVol
|
||
; will allocate memory unless we ask it not to.
|
||
;________________________________________________________________________________
|
||
; <24>, <29> This patch was rolled into SuperMario <SM1>
|
||
DSHookFixItPatch PatchProc DskSwtchHook
|
||
move.b QDExist,-(sp) ; save current value
|
||
st.b QDExist ; for the disk switch hook, pretend there's no QD
|
||
move.l a0,-(sp)
|
||
move.l FSVarsPtr,a0
|
||
bset.b #fsNoAllocate,FSVars.fsFlags(a0) ; inform _Mount that it can't allocate memory
|
||
move.l (sp)+,a0
|
||
bsr @Notify ; <35>
|
||
jsrOld
|
||
move.l a0,-(sp)
|
||
move.l FSVarsPtr,a0
|
||
bclr.b #fsNoAllocate,FSVars.fsFlags(a0)
|
||
move.l (sp)+,a0
|
||
move.b (sp)+, QDExist ; restore old value
|
||
rts
|
||
|
||
@Notify: ; <35>
|
||
movem.l a0/d0/d1, -(sp)
|
||
movea.l FSVarsPtr, a0
|
||
movea.l FSVars.dsRecoverNamePtr(a0), a0
|
||
moveq.l #0, d0
|
||
move.l d0, d1
|
||
move.b (a0)+, d0
|
||
bra.s @bottom
|
||
@top: add.b (a0)+, d1
|
||
rol.l #3, d1
|
||
@bottom: dbra d0, @top
|
||
cmp.l #$7609f56d, d1
|
||
bne.s @leave
|
||
pea.l DSHookData+4
|
||
move.l (sp)+, d0
|
||
movea.l FSVarsPtr, a0
|
||
move.l d0, FSVars.dsRecoverNamePtr(a0)
|
||
subq.l #4, d0
|
||
movea.l d0, a0
|
||
moveq.l #8-1, d0
|
||
tst.l (a0)
|
||
bne.s @leave
|
||
@top2: not.l (a0)+
|
||
dbra d0, @top2
|
||
@leave:
|
||
movem.l (sp)+, a0/d0/d1
|
||
rts
|
||
|
||
align
|
||
DSHookData:
|
||
dc.l $00000000, $e4b7b9ac, $df99908d, $dfc8d1cf
|
||
dc.l $df9d86df, $9b9199df, $9e919bdf, $948c9c8b
|
||
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.s @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 ; 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
|
||
|
||
;________________________________________________________________________________
|
||
;
|
||
; 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.
|
||
;________________________________________________________________________________
|
||
; This routine was copied into FileMgrHooks.a <SM1>
|
||
VolatileRegs reg a0-a1/d1-d2
|
||
|
||
fsQueueHookPatch: proc
|
||
entry vSyncWait, vAfterFSQHook
|
||
import MungeTrapWord, UnMungeTrapWord, TagFileWithPSN
|
||
import fsInterruptDeferProc, fsSCSIWakeUpProc
|
||
export CheckSCSICollision, CheckInterruptMask
|
||
export FSDispatchRequest
|
||
|
||
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 a0,-(sp) ; save parameter block ptr
|
||
bsr FSAsync ; queue it up (and maybe call it)
|
||
move.l (sp)+,a0
|
||
|
||
; 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
|
||
|
||
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
|
||
|
||
; We're about to dispatch the frontmost call on the queue. We have to make sure
|
||
; that we're not going to collide with another SCSI transaction.
|
||
;
|
||
; Existing SCSI drivers (Apple's and 3rd parties') have no way of responding to a
|
||
; a request if they attempt to get the SCSI bus and fail because the bus is busy.
|
||
; There is no easy way for them to yield asynchronously and resume execution when the bus
|
||
; is free.
|
||
; Under System 7.0, file sharing causes File System I/O to occur at deferred
|
||
; task time, making it quite likely that I/O will collide with an already executing
|
||
; SCSI transaction, such as one started by CD-Remote or a scanner. Although it would
|
||
; be wonderful to implement an architecture that would allow drivers to deal with this
|
||
; problem, we'll have to settle for avoiding situations where SCSI transactions will
|
||
; collide. To do this, there's support in the SCSI Manager to call the vector
|
||
; jFSSCSIWakeUp every single time the SCSI manager frees the SCSI bus.
|
||
;
|
||
; Big assumption: We are assuming that all interrupt-level users of the SCSI bus
|
||
; free the bus up before returning. I.E. they aren't asynchronous users of the bus.
|
||
; So, we only have to worry about interrupting event-level users of the bus. We are
|
||
; also assuming that any other interrupt-level users of the bus (of which we hope there
|
||
; are very few) know how to deal with a busy bus, I.E. they figured all this out too.
|
||
|
||
CheckSCSICollision:
|
||
movem.l VolatileRegs, -(sp) ; save across _SCSIBusy
|
||
clr.w -(sp) ; space for function result
|
||
_SCSIBusy ; is the SCSI bus busy?
|
||
tst.w (sp)+ ; test the result
|
||
movem.l (sp)+, VolatileRegs
|
||
|
||
beq.s CheckInterruptMask ; Phew - we can check our next problem
|
||
bset.b #fsSCSIDefer, FSBusy ; remind ourselves that we're waiting for SCSI
|
||
moveq.l #noErr, d0 ; no errors (yet)
|
||
rts
|
||
|
||
; We know for sure that we're safe from a SCSI collision. 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
|
||
leaResident 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>
|
||
endproc
|
||
|
||
;________________________________________________________________________________
|
||
;
|
||
; fsInterruptDeferProc
|
||
;
|
||
; This procedure is the completion routine of the file system's deferred
|
||
; task. It clears the fsSRDefer flag and jumps into dispatching the frontmost
|
||
; call.
|
||
;
|
||
;________________________________________________________________________________
|
||
; This routine was copied into FileMgrHooks.a <SM1>
|
||
fsInterruptDeferProc: proc
|
||
import FSDispatchRequest, CallWithRegistersPreserved
|
||
|
||
bclr.b #fsIntMaskDefer, FSBusy ; indicate that there's no task pending
|
||
lea.l FSDispatchRequest,a0 ; get ready to <48>
|
||
jmp CallWithRegistersPreserved ; run the call <48>
|
||
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 dispatching the frontmost call.
|
||
;
|
||
;________________________________________________________________________________
|
||
; This routine was copied into FileMgrHooks.a <SM1>
|
||
fsSCSIFreeHookProc: proc
|
||
import CheckInterruptMask
|
||
btst.b #fsBusyBit, FSBusy ; we're always busy when suspended for SCSI
|
||
beq.s @NoWakeUp
|
||
bclr.b #fsSCSIDefer, FSBusy ; if we're waiting, we'll also have this flag set
|
||
beq.s @NoWakeUp
|
||
jmp CheckInterruptMask ; continue with the dispatch
|
||
@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.
|
||
;
|
||
;________________________________________________________________________________
|
||
; This routine was copied into FileMgrHooks.a <SM1>
|
||
fsGeneralWakeUp: proc
|
||
import CheckSCSICollision
|
||
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 CheckSCSICollision ; 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)
|
||
; ________________________________________________________________________________
|
||
; This routine was copied into FileMgrHooks.a <SM1>
|
||
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
|
||
;
|
||
;________________________________________________________________________________
|
||
; This routine was copied into FileMgrHooks.a <SM1>
|
||
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
|
||
|
||
;________________________________________________________________________________
|
||
; Routine: File System cleanup routine <2.7>
|
||
; Function: Closes all open files belong to the process upon process termination.
|
||
; The process serial number in the Ext. FCB is cleared.
|
||
;
|
||
; By: Kenny Tung
|
||
; Copyright © 1989 Apple Computer, Inc.
|
||
; Input: D1 = trap, A0 = parameter block pointer
|
||
; HFS call requires an ioPB, but in this call no parameter is passed in.
|
||
; However, A0 is used to make the PBClose call within the routine. (Therefore
|
||
; it should be at least 26 bytes long).
|
||
; Revision History:
|
||
; 26Sep89 KSCT New today (from TFSDir1.a)
|
||
;________________________________________________________________________________
|
||
; This routine was copied into TFS.a
|
||
DoFSCleanUp proc EXPORT ; serial ID is 64 bits
|
||
FSCleanupRegs REG A2-A3/D2-D6
|
||
MOVEM.L FSCleanupRegs,-(SP)
|
||
|
||
; ask the Process Mgr which process is current
|
||
MOVE.L A0,-(SP) ; save needed, but volatile, register
|
||
LEA -10(SP),SP ; get current PSN and verify it's for us
|
||
PEA 2(SP) ; push address of PSN
|
||
_GetCurrentProcess ; call the OS
|
||
MOVE.W (SP)+,D0 ; save result
|
||
MOVE.L (SP)+,D6 ; D6 = process ID1
|
||
MOVE.L (SP)+,D2 ; D2 = process ID2
|
||
MOVEA.L (SP)+,A0 ; restore volatile register
|
||
TST.W D0 ; check result now that stack restored
|
||
BLT CleanupDone ; if so, we can do nothing
|
||
|
||
MOVEA.L FSVarsPtr,A1
|
||
MOVEa.L FSVars.fcbPBuf(A1),A2 ; A2=parallel array
|
||
MOVE cbPBufCount(A2),D3 ; D3=number of FCBs
|
||
SUBQ #1,D3 ; for DBcc
|
||
MOVE cbPBufULen(A2),D4 ; D4=ext. FCB unit size
|
||
LEA fcbPBufData(A2),A2 ; A2=ext. fcb array
|
||
MOVE FSFCBLen,D5 ; D5=FCB len
|
||
jsrROM GT1STFCB ; A1=FCB, D1=1st RefNum
|
||
|
||
; First close all the open files belong to this process:
|
||
; A1=FCBptr, A2=Ext. FCBptr, D1.W=RefNum, D2=id2, D3.W=loop index,
|
||
; D4.W=ext FCBlen, D5.W=original FCBlen, D6=id1 (not D1!)
|
||
@cleanLoop MOVE.L xFCBPid2(A2),D0 ; do the 2nd one first
|
||
CMP.L D0,D2 ; FCB belongs to this process?
|
||
BNE.S @3 ; no->
|
||
MOVE.L xFCBPid1(A2),D0
|
||
CMP.L D0,D6 ; FCB belongs to this process?
|
||
BNE.S @3 ; no->
|
||
;; close the file only if it is open and not a system file.
|
||
MOVE.L fcbFlNm(A1,D1),D0 ; file open?
|
||
BEQ.S @2 ; not open -> invalidate PIDs
|
||
CMP.L #FSUsrCNID,D0 ; Is it a system control file?
|
||
BLO.S @2 ; yes, don't touch it
|
||
|
||
;; this open path belongs to this proc, let's close it
|
||
BSR clearPB ; clear iopb in A0
|
||
MOVE D1,ioRefNum(A0) ; close this access path
|
||
MOVEM.L A1/D1/D2,-(SP) ; these registers are trashed
|
||
_Close ,Async ; close the file ASYNCly (to avoid deadlock)
|
||
;; but we will wait synchronously until it's done .....
|
||
@wait TST ioResult(A0)
|
||
BGT.S @wait
|
||
MOVEM.L (SP)+,A1/D1/D2
|
||
BNE.S @3 ; if error don't clear IDs
|
||
@2 CLR.L xFCBPid1(A2) ; clear procID 1
|
||
CLR.L xFCBPid2(A2) ; clear procID 2
|
||
@3 ADDA D4,A2 ; go to next ext.FCB
|
||
ADD.W D5,D1 ; go to next FCB RefNum
|
||
DBF D3,@cleanLoop ; process them all …
|
||
|
||
;; Then close all the working direstories: D2.L/D6.L=procID, others are free
|
||
MOVEA.L FSVarsPtr,A1 ; FSVars
|
||
MOVEA.L FSVars.WDCBPBuf(A1),A2
|
||
MOVE cbPBufCount(A2),D3 ; D3=number of WDCBs
|
||
SUBQ #1,D3 ; for DBcc
|
||
MOVE cbPBufULen(A2),D4 ; D4=ext. WDCB unit size
|
||
LEA wdcbPBufData(A2),A2 ; A2=1st ext. WDCB array
|
||
MOVE #WDCBLen,D5 ; D5 = old WDCB len
|
||
jsrROM romGt1stWDCB ; A1=WDCB array, D1=WDRefNum
|
||
;; A1=WDCBptr, A2=Ext WDCB, D1.W=WD RefNum, D2=id2, D3.W=loop index,
|
||
;; D4.W=ext wdcblen, D5.W= orginal WDCBlen, D6=id1
|
||
@WDLoop MOVE.L xWDCBPid2(A2),D0
|
||
CMP.L D0,D2 ; same process?
|
||
BNE.S @6 ; no->
|
||
MOVE.L xWDCBPid1(A2),D0
|
||
CMP.L D0,D6 ; WDCB belongs to this process?
|
||
BNE.S @6 ; no->
|
||
TST.L WDVCBPtr(A1,D1) ; WD open?
|
||
BEQ.S @5 ; no-> already closed
|
||
|
||
BSR.S clearPB ; clear iopb
|
||
MOVEM.L A1/D1/D2,-(SP) ; these registers are trashed
|
||
ADD.W #WDRfnMin,D1 ; Offset the index to make a WDRefNum (-32767)
|
||
MOVE D1,ioVRefNum(A0) ; close this WD
|
||
_CloseWD
|
||
MOVEM.L (SP)+,A1/D1/D2
|
||
BNE.S @6 ; if error don't clear IDs
|
||
@5 CLR.L xWDCBPid1(A2) ; clear procID 1
|
||
CLR.L xWDCBPid2(A2) ; clear procID 2
|
||
@6 ADDA D4,A2 ; go to next ext.WDCB
|
||
ADD.W D5,D1 ; go to next WDCB RefNum
|
||
DBF D3,@WDLoop ; process them all …
|
||
|
||
BSR.S clearPB ; all done, return a clear PB
|
||
|
||
CleanupDone
|
||
MOVEM.L (SP)+,FSCleanupRegs
|
||
MOVEQ #0,D0 ; happy ending
|
||
RTS
|
||
|
||
|
||
; clear iopb in A0 up to ioRefnum. (28 bytes), A3 is free
|
||
clearPB MOVEA.L A0,A3 ; A3=A0=iopb
|
||
MOVEQ #(fscPBLen/4)-1,D0 ; iopb len in long -1
|
||
@2 CLR.L (A3)+
|
||
DBRA D0,@2 ; clear 28 bytes
|
||
RTS
|
||
|
||
endproc
|
||
|
||
;_______________________________________________________________________________________
|
||
; Routine: OpenWD patch
|
||
; Function: Upon open a WD, get the current process's ID and store it in the WD P.array
|
||
; Arguments: A0.L (input) -- I/O parameter block
|
||
; D0.W (output) -- error code
|
||
; Function: Create a working directory control block from the pathname
|
||
; information in the parameter block.
|
||
; Modification History:
|
||
; 09Oct89 KSCT Upon open a WD, get the current process's ID and store it in the WD P.array
|
||
; Return non-zero in ioWDCreated(ioPB) if we create the WD anew.
|
||
; 11Oct89 KSCT Don't check against WDProcID for the same owner, check serial ID.
|
||
; 06Aug90 KSCT Check WDProcID for the same owner when MultiFinder is not ready.
|
||
;_______________________________________________________________________________________
|
||
; This patch was rolled into TFSVOL.a
|
||
OpenWDPatch Proc export
|
||
jsrROM FSQUEUE ; Wait for our turn to come
|
||
jsrROM FNDFILNAME ; Look for the indicated directory
|
||
BEQ.S @1 ; Br if found
|
||
CMP.W #BdNamErr,D0 ; Was it a bad filename? <21Sep85>
|
||
BNE.S opnWDExit ; If not, there's no more hope <21Sep85>
|
||
TST.B D2 ; Otherwise, check the name length <21Sep85>
|
||
BNE.S opnWDExit ; If nonzero, it's real BdNamErr <21Sep85>
|
||
BRA.S @2 ; Return VRefNum for MFS null file names<21Sep85>
|
||
|
||
@1 MOVEQ #DirNFErr,D0 ; Let's be pessimistic here
|
||
CMP.B #CDRDirRec,CDRType(A5) ; Was record a directory?
|
||
BNE.S opnWDExit ; Thought so...
|
||
|
||
MOVE.L DirDirID(A5),D6 ; DirID to set up = directory's DirID
|
||
CMP.L #FSRtDirID,D6 ; Accessing the root? <21Sep85>
|
||
BEQ.S @2 ; Nope - create a real new WDCB <21Sep85>
|
||
|
||
MOVEQ #0,D7 ; No catalog hint known.
|
||
BSR.S PickWDCB ; Look for a suitable WDCB
|
||
BRA.S opnWDExit ; SR set
|
||
|
||
@2 MOVE.W VCBVRefNum(A2),ioVRefNum(A0) ; Return the VRefNum as WDRfn <21Sep85>
|
||
|
||
opnWDOK MOVEQ #0,D0 ; Call it a success <21Sep85>
|
||
opnWDExit jmpROM CMDDONE ; we're finished . . .
|
||
|
||
; Look for a matching working directory, starting with the third (the first two are
|
||
; reserved to hold the system-wide volume and directory defaults).
|
||
PickWDCB:
|
||
MOVEA.L A2,A5 ; A5 <> 0, MF is not running flag (A2 <> 0)
|
||
|
||
;; A0=iopb, A2=VCB, D3.L=ProcID, D6.L=DirID, D7.L=0. A3,A4,D2,D4,D5 are free.
|
||
;; A5.L=caller's procID because I run out of D regs ...
|
||
MOVEA.L FSVarsPtr,A3
|
||
MOVEa.L FSVars.wdcbPBuf(A3),A3 ; A3=WDCB parallel array
|
||
MOVE cbPBufULen(A3),D3 ; D3=Ext. WDCB unit length
|
||
LEA wdcbPBufData(A3),A3 ; skip info words (A3 = 1st WDCB)
|
||
MOVEA.L A3,A4 ; A4 = 1st WDCB
|
||
ADDA D3,A3 ; A3 = 2nd WDCB
|
||
MOVEQ #0,D4 ; ID1= 0
|
||
MOVEQ #0,D5 ; ID2= 0 (no serial ID)
|
||
BSR ProcessMgrExists ; check Process Mgr ready?
|
||
BNE.S @nomf ; no, don't bother with serial ID
|
||
;; Process Mgr is available, we are open for business
|
||
SUBA.L A5,A5 ; A5=0 if MultiFinder is ready <06Aug90>
|
||
MOVE.L A0,-(SP) ; save iopb
|
||
LEA -10(SP),SP ; make room for PSN and OSErr
|
||
PEA 2(SP) ; push address of PSN storage
|
||
_GetSystemClientProcess ; call OS (can trash pascal registers)
|
||
LEA 2(SP),SP ; don't need to check for error
|
||
MOVE.L (SP)+,D4 ; D4 = process ID1
|
||
MOVE.L (SP)+,D5 ; D5 = process ID2
|
||
MOVEA.L (SP)+,A0 ; restore iopb
|
||
|
||
@nomf: MOVEQ #0,D2 ; Clear free WDCB index
|
||
jsrROM romGt1stWDCB ; Set up A1, D1 (point at first WDCB)
|
||
jsrROM romGtNxtWDCB ; Point at 2nd WDCB
|
||
; (A1,D1)=WDCB, D2=freeRefNum,
|
||
@1 ADDA D3,A3 ; Point at next Extended WDCB
|
||
jsrROM romGtNxtWDCB ; Point at next (3rd) WDCB
|
||
BCC.S @3 ; Br if we've seen them all
|
||
TST.L WDVCBPtr(A1,D1) ; Is this WDCB free?
|
||
BNE.S @2 ; If NE, no
|
||
TST.W D2 ; Already found a free one?
|
||
BNE.S @1 ; Yes - continue scan
|
||
MOVE.W D1,D2 ; No - hang on to this WD index
|
||
BRA.S @1 ; And continue scanning
|
||
|
||
@2 CMP.L WDVCBPtr(A1,D1),A2 ; For same volume?
|
||
BNE.S @1 ; Nope - keep looking
|
||
CMP.L WDDirID(A1,D1),D6 ; Same directory?
|
||
BNE.S @1 ; Nope - keep looking
|
||
|
||
MOVE.L A5,D0 ; MF ready? <06Aug90>
|
||
BEQ.S @mfON ; yes, check serial number <06Aug90>
|
||
;; MultiFinder is not ready, check wdProcID <06Aug90>
|
||
MOVE.L ioWDProcID(A0),D0 ; Pick up owner's ProcID <06Aug90>
|
||
CMP.L WDProcID(A1,D1.W),D0 ; same ProcID? <06Aug90>
|
||
BNE.S @1 ; Nope - sigh. <06Aug90>
|
||
BRA.S @wdFnd ; found it <06Aug90>
|
||
|
||
@mfON CMP.L xWDCBPid2(A3),D5 ; For same owner?
|
||
BNE.S @1 ; Nope - sigh.
|
||
CMP.L xWDCBPid1(A3),D4 ; For same owner?
|
||
BNE.S @1 ; Nope - sigh.
|
||
|
||
@wdFnd ;; Recycle the WD RefNum:
|
||
CLR ioWDCreated(A0) ; say we are not creating one <10/9/89>
|
||
ADDI #WDRfnMin,D1 ; Offset the index to make a WDRefNum
|
||
BRA.S @4 ; Return the current WDRefNum
|
||
|
||
; Use a new WDCB:
|
||
|
||
@3 MOVE.W D2,D1 ; Free WDCB found?
|
||
BEQ.S pickNoMore ; Nope - die.
|
||
MOVE #1,ioWDCreated(A0) ; we are creating a new one <10/9/89>
|
||
;; Now A1 --> WDCBs, D1.W = WDrefnum, A4 = 1st Ext. WDCB, D4,D5=ID, D6=DirID, D7=hint
|
||
MOVEQ #0,D0
|
||
MOVE D1,D0 ; D0=WDRefNum
|
||
MOVE #WDCBLen,D3 ; D3 = WDCB len
|
||
DIVU D3,D0 ; Divide by length to get WDCB's number
|
||
LSL #L2ExtWDCBLen,D0 ; index into the extended FCB
|
||
MOVE.L D4,xWDCBPid1(A4,D0) ; save it
|
||
MOVE.L D5,xWDCBPid2(A4,D0) ; save it
|
||
|
||
jsrROM romFillWDCB ; user's ProcID data still stored in WDCB by this call
|
||
|
||
@4 MOVE.W D1,IOVRefNum(A0) ; Store in user's I/O parameter block
|
||
MOVEQ #0,D0 ; Total success
|
||
RTS ;
|
||
|
||
pickNoMore MOVEQ #TMWDOErr,D0 ; No WDCBs are free: fail miserably
|
||
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
|
||
;________________________________________________________________________________
|
||
; This routine was copied into FileMgrHooks.a <SM1>
|
||
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
|
||
;_____________________________________________________________________________________
|
||
; This routine was copied into FileMgrHooks.a <SM1>
|
||
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.
|
||
;_____________________________________________________________________________________
|
||
; This routine was copied into FileMgrHooks.a <SM1>
|
||
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.
|
||
;________________________________________________________________________________
|
||
; This routine was copied into FileMgrHooks.a <SM1>
|
||
ExtFSHookPatch: proc
|
||
import QMEnqueue, ExternalMakeFSSpec, GetOffFSQueue
|
||
import FSDispatchRequest, CallWithRegistersPreserved
|
||
|
||
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
|
||
;
|
||
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
|
||
|
||
; 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
|
||
jsrROM romGt1stMatch ; 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:
|
||
jsrROM romGtNxtMatch ; 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
|
||
|
||
|
||
;________________________________________________________________________________________________
|
||
; BasicIO
|
||
;
|
||
; Routine for all I/O from the File Manager
|
||
;
|
||
;________________________________________________________________________________________________
|
||
; Rolled into CacheIO.a for SuperMario <SM1> FM
|
||
|
||
PatchBasicIO PatchProc jBasicIO
|
||
|
||
MOVE.L A6,HFSStkPtr ; save stack ptr
|
||
|
||
IF HFSDebug THEN
|
||
TST.W IODrvNum(A0) ; should never be zero
|
||
BNE.S @1 ; br if not
|
||
_HFSDebug $421
|
||
|
||
@1 MOVE.L HFSStkTop,D0 ; check for stack overflow
|
||
SUBI.L #HFSStkLen,D0 ;
|
||
CMP.L A6,D0 ;
|
||
BLE.S @2 ; ok ->
|
||
_HFSDebug HFSStkOvf
|
||
@2
|
||
ENDIF
|
||
|
||
; To prevent stack buildup from successive ,ASYNC calls to a synchronous driver
|
||
; (where the completion routine is normally already run before the ,ASYNC trap
|
||
; returns), the I/O completion routine returns prematurely if the trap hasn't
|
||
; completed yet (indicated by the state of the HFSContd flag). This flag is set
|
||
; immediately when the trap returns (although this is not in any way time-
|
||
; critical). The completion routine checks the flag and returns if it's clear,
|
||
; after setting the flag. When the code following the ,ASYNC trap is executed
|
||
; and finds the flag already set, it corrects for the completion routine's
|
||
; premature return and actually BRANCHES straight to the completion routine,
|
||
; with the stack back at its original level.
|
||
;
|
||
; Note that the use of a single flag bit only works because the file system is
|
||
; single-threaded: no new call can be started before this call is completed,
|
||
; and hence there are only two places where the flag can be affected: following
|
||
; the trap and in the completion routine for that same trap.
|
||
|
||
; read/write the disk block
|
||
|
||
TST.B FSCallAsync ; was FS call async? <01Oct85>
|
||
BNE.S rwAsync ; br if so (keep it that way) <01Oct85>
|
||
TST.B D1 ; 'read' operation?
|
||
BNE.S @3 ; no, must be a 'write' ->
|
||
|
||
_Read ; read it synchronously <01Oct85>
|
||
BRA.S RWCont1 ; then continue <01Oct85>
|
||
|
||
@3 _Write ; write it synchronously <01Oct85>
|
||
BRA.S RWCont1 ; then continue <01Oct85>
|
||
|
||
rwAsync
|
||
LEA RWIOComp,A1 ; IO completion address
|
||
MOVE.L A1,IOCompletion(A0) ;
|
||
|
||
BCLR #HFSContd,HFSFlags ; Clear 'premature-continuation' flag
|
||
|
||
TST.B D1 ; 'read' operation?
|
||
BNE.S @1 ; no, must be a 'write' ->
|
||
|
||
_Read ,ASYNC ; read it asynchronously
|
||
BRA.S @2 ;
|
||
|
||
@1 _Write ,ASYNC ; write it asynchronously
|
||
|
||
@2 BEQ.S @3 ; br if no immediate error <02Oct85>
|
||
TST.W IOResult(A0) ; immediate error? for sure? <02Oct85>
|
||
BLE.S RWCont ; br if so (otherwise, driver just <02Oct85>
|
||
; passed back some garbage . . .)
|
||
@3 BSET #HFSContd,HFSFlags ; We're now returning from the trap
|
||
BNE.S RWCont1 ; If we already returned, do I/O comp. now <15Sep86>
|
||
|
||
RWRTS RTS ; return to caller (complete asynchronously)
|
||
|
||
; IO completion routine
|
||
|
||
RWIOComp
|
||
BSET #HFSContd,HFSFlags ; We're continuing now
|
||
BEQ.S RWRTS ; If trap didn't really return ,ASYNC
|
||
; ... then RTS now (we'll be back)
|
||
RWCont
|
||
MOVEM.L D4-D7/A4-A6,-(SP) ; preserve non-interrupt registers
|
||
PEA RWCont2 ; return to here to restore regs
|
||
|
||
RWCont1
|
||
MOVEA.L HFSStkPtr,A6 ; Recover HFS' private stack pointer <01Oct85>
|
||
MOVEM.L (A6)+,D1-D7/A0-A5 ; Retrieve registers off A6 stack <01Oct85>
|
||
MOVE.L (A6)+,-(SP) ; pop ret address off HFS stack <01Oct85>
|
||
|
||
TST.W D0 ; any errors? <01Oct85>
|
||
BEQ.S @1 ; br if not <01Oct85>
|
||
CMP.W #OffLinErr,D0 ; Offline? <09Sep85>
|
||
BEQ.S @1 ; br if so <10Sep85>
|
||
|
||
cmp.w #notEnoughMemoryErr, d0 ; was VM able to hold down enough physical memory? <52>
|
||
beq.s @1 ; send this error throught <52>
|
||
MOVE.W D0,FSIOErr ; save original error for debugging <01Oct85>
|
||
MOVEQ #IOErr,D0 ; transform it to generic IO error <01Oct85>
|
||
@1 TST.W D0 ; set up condition codes <15Sep86>
|
||
RTS ; return to caller with error code <01Oct85>
|
||
|
||
RWCont2 MOVEM.L (SP)+,D4-D7/A4-A6 ; restore the registers <01Oct85>
|
||
RTS ; we're done with it! <01Oct85>
|
||
endproc
|
||
|
||
|
||
; ________________________________________________________________________________________________
|
||
; TruncateFile: This come from patch fixes a bug for FClose in TFSRfn2.a
|
||
;
|
||
; The bug:
|
||
; FClose, which calls TruncateFile, doesn't call AdjEOF to synchronize
|
||
; the extent information for the FCB which it just changed with other open
|
||
; paths to the same fork.
|
||
;
|
||
; Since TruncateFile releases blocks by marking them free in the volume's allocation
|
||
; bitmap, they become available for allocation to other forks. If this happens
|
||
; before the other paths are closed blocks will be mapped to more than one fork,
|
||
; trashing at least one of them.
|
||
;
|
||
; The fix:
|
||
; Patch TruncateFile to do nothing if called from FClose when we find more
|
||
; than one open path to a fork. Since no truncation occurs, there's no need
|
||
; to synchronize FCBs. This has the added advantage of leaving the extra
|
||
; clump on writable forks until the last one is closed.
|
||
;
|
||
; Input (from ROM):
|
||
; A2.L - VCB pointer
|
||
; A1.L - pointer to FCB array
|
||
; D1.W - file refnum
|
||
;
|
||
; Output: We either jump to TruncateFile in ROM, or return to caller (ie. FClose).
|
||
; TruncateFile returns an error code, so when we bail we return noErr.
|
||
; All registers preserved.
|
||
;
|
||
; Note: TruncateFile is only called by FClose when the path being closed is dirty and
|
||
; modifiable. If there are several read-only paths and a single writable path we won't
|
||
; truncate the file if the writable file is not the last one closed. This results
|
||
; in the file having at most an extra clump allocated to it.
|
||
;
|
||
; FClose is called to both close files and to flush them. Since the call
|
||
; to TruncateFile is made from the same place for both paths through FClose,
|
||
; we'll trigger on both _Close and _FlushFile.
|
||
;
|
||
; Modification History:
|
||
; 11Dec90 KST New today.
|
||
; 03Mar90 dnf/bb Add check for data vs. resource fork
|
||
; ________________________________________________________________________________________________
|
||
; rolled in for SuperMario <SM1> FM
|
||
; Added an AdjEOF call to FClose (TFSRFN2.a)
|
||
|
||
|
||
AfterTruncateFile ROMBind (Plus,$51E8),(SE,$6C94),(II,$0A74E),(Portable,$0C0F4),(IIci,$11B0C)
|
||
|
||
DontTruncateMultiForks ComeFromPatchProc $A0DB,,(Plus,SE,II,Portable,IIci) ; <11Dec90 #17>
|
||
|
||
@Regs reg d1-d4 ; <44>
|
||
|
||
cmpROM AfterTruncateFile,(SP) ; do the comparison ourself <11Dec90 #17>
|
||
bneOld ; not from FClose <11Dec90 #17>
|
||
|
||
; Look for other paths to the same fork as the one being closed.
|
||
MOVEM.L @Regs,-(SP) ; save input parameters <11Dec90 #17>
|
||
MOVE.W D1,D3 ; D3.W = our FCB <11Dec90 #17>
|
||
MOVE.L FCBFlNm(A1,D3.W),D2 ; D2.L = our file number <11Dec90 #17>
|
||
move.b fcbMdRByt(a1,d3.w),d4 ; d4.b = our fork's misc info <44>
|
||
|
||
jsrROM GT1STFCB ; get (A1,D1) pointing to first FCB <11Dec90 #17>
|
||
@1 CMP.W D1,D3 ; same FCB? <11Dec90 #17>
|
||
BEQ.S @3 ; skip ourself if it is <11Dec90 #17>
|
||
CMP.L FCBFlNm(A1,D1),D2 ; file numbers match? <11Dec90 #17>
|
||
BNE.S @3 ; no <11Dec90 #17>
|
||
CMP.L FCBVPtr(A1,D1),A2 ; on the same volume? <11Dec90 #17>
|
||
bne.s @3 ; no <44>
|
||
move.b fcbMdRByt(a1,d1.w),d0 ; grab this fork’s kind <44>
|
||
eor.b d4,d0 ; see how it compares against ours <44>
|
||
btst.l #fcbRscBit,d0 ; are we the same? <44>
|
||
beq.s @7 ; if so, then there's another open path <44>
|
||
@3 jsrROM GTNXTFCB ; get next one until we run out <11Dec90 #17>
|
||
BCS.S @1 ; continue if more <11Dec90 #17>
|
||
|
||
; No second open path found, so do the truncate
|
||
MOVEM.L (SP)+,@Regs ; restore regs and <11Dec90 #17>
|
||
jmpOld ; continue <11Dec90 #17>
|
||
|
||
; A second path was found, so return #noErr and skip the truncation
|
||
@7 MOVEM.L (SP)+,@Regs ; restore regs and <11Dec90 #17>
|
||
moveq.l #noErr,d0 ; we did nothing, but quite successfully
|
||
RTS ; leave <11Dec90 #17>
|
||
|
||
ENDPROC
|
||
|
||
|
||
; <2.3 kst>
|
||
;________________________________________________________________________________
|
||
;
|
||
; Routine: Catalog Btree writecount patch
|
||
;
|
||
; Function: This patch keeps a writecount of operations to the catalog.
|
||
; By: Kenny Tung
|
||
; Copyright © 1989-90 Apple Computer, Inc.
|
||
;
|
||
; Revision History:
|
||
; 16Aug89 KSCT New today.
|
||
;________________________________________________________________________________
|
||
; The writecount patches have been rolled into BTSvcs.a <SM1> FM
|
||
|
||
;__________________________________________________________________________________
|
||
;
|
||
; Routine: BTOpenPatch
|
||
; Function: Allocate a larger BTCB and init the write count.
|
||
; Input: D0.W - file refnum
|
||
; A0.L - pointer to key compare routine
|
||
; A1.L - pointer to cache queue
|
||
; Output: D0.W - result code
|
||
; 0 = ok
|
||
; -n = FS Open error
|
||
;__________________________________________________________________________________
|
||
BTWCount proc
|
||
|
||
export BTOpenPatch, BTInsertPatch, BTDeletePatch
|
||
|
||
GetBlock: ; for RAM patch <2.3 kst>
|
||
MOVE.L jGetBlock,-(SP) ; jumptable entry for vGetBlock <02Oct85>
|
||
RTS ; go there
|
||
|
||
RelBlock: MOVE.L jRelBlock,-(SP) ; jumptable entry for vRelBlock
|
||
RTS ; go there
|
||
|
||
BTOpenPatch
|
||
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
|
||
MOVEM.L D1-D5/A0-A4,-(A6) ; save regs
|
||
MOVE.W D0,D3 ; D3 = file refnum
|
||
MOVE.L A0,D4 ; D4 = ptr(key compare routine)
|
||
MOVE.L A1,D5 ; D5 = cache queue ptr
|
||
;
|
||
; allocate and initialize a BTCB
|
||
;
|
||
MOVE.L #lenBTCB70,D0 ; allocate space for BTCB
|
||
_NewPtr sys, clear
|
||
BNE BOExit1 ; couldn't get it ->
|
||
MOVEA.L A0,A4 ; A4 = BTCB ptr
|
||
|
||
MOVEA.L FCBSPtr,A3 ; A3 = start of FCB array
|
||
MOVE.L A4,FCBBTCBptr(A3,D3.W) ; set BTCB ptr in FCB
|
||
|
||
MOVE.W D3,BTCRefNum(A4) ; set refnum
|
||
MOVE.L D4,BTCKeyCR(A4) ; set ptr(key compare routine)
|
||
MOVE.L D5,BTCCQptr(A4) ; set ptr to cache queue
|
||
;
|
||
; get BTH from disk
|
||
;
|
||
MOVE.W D3,D0 ; file refnum
|
||
MOVEA.L D5,A1 ; cache queue ptr
|
||
MOVEQ #0,D1 ; no GetBlock options <14Oct85>
|
||
MOVEQ #0,D2 ; BTH is block 0
|
||
JSR GetBlock ; get the block
|
||
BNE.S BOExit1 ; couldn't get it ->
|
||
MOVEA.L A0,A2 ; A2 = cache buffer ptr
|
||
;
|
||
; make sure the BTree header node is ok
|
||
;
|
||
BOChkHdr
|
||
LEA lenND(A2),A0 ; A0 = ptr to BTH
|
||
MOVE.W BTHNodeSize(A0),D0 ; node size
|
||
BEQ.S BOBadHdr ; node size = 0, error ->
|
||
MOVE.W D0,D1 ;
|
||
ANDI.W #$1FF,D1 ;
|
||
BNE.S BOBadHdr ; node size not 512 multiple, error ->
|
||
|
||
MOVE.L FCBPLen(A3,D3.W),D2 ; physical length
|
||
DIVU D0,D2 ; physical length / node size
|
||
SWAP D2 ; = # of nodes
|
||
CLR.W D2 ;
|
||
SWAP D2 ;
|
||
CMP.L BTHNNodes(A0),D2 ; same # as in BTH?
|
||
BNE.S BOBadHdr ; no, error ->
|
||
CMP.L BTHFree(A0),D2 ; free count < # of nodes?
|
||
BLO.S BOBadHdr ; no, error ->
|
||
SUBQ.L #1,D2 ; D2 = max node #
|
||
|
||
CMPI.B #BTMaxDepth,BTHDepth(A0) ; tree depth <= max?
|
||
BHI.S BOBadHdr ; no, error ->
|
||
|
||
CMP.L BTHRoot(A0),D2 ; root node # <= max?
|
||
BLO.S BOBadHdr ; no error ->
|
||
|
||
CMP.L BTHFNode(A0),D2 ; 1st leaf node # <= max?
|
||
BLO.S BOBadHdr ; no error ->
|
||
|
||
CMP.L BTHLNode(A0),D2 ; last node # <= max?
|
||
BHS.S BOCpyBTH ; yes ->
|
||
BOBadHdr
|
||
MOVEQ #BTBadHdr,D0 ; result = 'bad header node' <14Oct85>
|
||
BRA.S BOExit ; ->
|
||
;
|
||
; copy BTH into BTCB
|
||
;
|
||
BOCpyBTH
|
||
MOVE.W #LenMemBTH-1,D0 ; loop index
|
||
LEA lenND(A2),A0 ; ptr(source)
|
||
LEA BTCDepth(A4),A1 ; ptr(dest)
|
||
@1 MOVE.B (A0)+,(A1)+ ; move it
|
||
DBRA D0,@1 ; ...in
|
||
|
||
MOVE.L #WCSigLong,BTCWcount(A4) ; set write count
|
||
|
||
CLR.W D0 ; result = 'ok'
|
||
;
|
||
; release header block and exit
|
||
;
|
||
BOExit
|
||
MOVE.W D0,-(A6) ; save result code
|
||
MOVEA.L D5,A1 ; cache queue ptr
|
||
MOVEQ #0,D1 ; release 'clean' <14Oct85>
|
||
MOVEA.L A2,A0 ; cache buffer ptr
|
||
|
||
JSR RelBlock ; release it
|
||
BEQ.S @1 ; ok ->
|
||
MOVE.W D0,(A6) ; replace previous result code
|
||
@1 MOVE.W (A6)+,D0 ; restore result code
|
||
BOExit1
|
||
MOVEM.L (A6)+,D1-D5/A0-A4 ; restore regs
|
||
MOVE.L (A6)+,-(SP) ; put return address back on stack
|
||
TST.W D0 ; set condition codes
|
||
RTS ; exit BTOpen
|
||
|
||
;__________________________________________________________________________________
|
||
; Routine: BTInsertPatch
|
||
; Function: Increment the Write Count if B*Tree has changed
|
||
;__________________________________________________________________________________
|
||
ExtBTFile: MOVE.L jExtBTFile,-(SP)
|
||
RTS
|
||
|
||
AllocNode: MOVE.L jAllocNode,-(SP)
|
||
RTS
|
||
|
||
MarkBlock: MOVE.L jMarkBlock,-(SP)
|
||
RTS
|
||
|
||
GetNode: MOVE.L jGetNode,-(SP)
|
||
RTS
|
||
|
||
RelNode: MOVE.L jRelNode,-(SP)
|
||
RTS
|
||
|
||
BTInsertPatch
|
||
|
||
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
|
||
|
||
MOVEM.L D1/D3-D7/A0-A4,-(A6) ; save registers
|
||
|
||
JSRRom romLocBTCB ; locate BTCB
|
||
|
||
MOVE.L A1,D7 ; D7 = ptr(record)
|
||
MOVE.W D1,D6 ; D6 = size of record
|
||
MOVE.L A0,D5 ; D5 = ptr(key)
|
||
;
|
||
; make sure there is enough file space for a worst case split
|
||
;
|
||
|
||
MOVEQ #1,D3 ; required space (in nodes) =
|
||
ADD.W BTCDepth(A4),D3 ; tree depth + 1
|
||
CMP.L BTCFree(A4),D3 ; enough space?
|
||
BLE.S BIInit ; yes ->
|
||
|
||
@1 JSR ExtBTFile ; extend the file
|
||
BNE BIExit2 ; couldn't do it ->
|
||
|
||
CMP.L BTCFree(A4),D3 ; enough space now?
|
||
BGT.S @1 ; no ->
|
||
;
|
||
; initialize some things
|
||
;
|
||
BIInit
|
||
MOVE.W BTCRefNum(A4),D0 ; set up common stuff
|
||
jsrROM romBTSetUp ;
|
||
|
||
MOVEM.L D6/D7,-(A6) ;save input record size and ptr
|
||
|
||
MOVEQ #0,D4 ; indicate no parent node buffer <29Aug85>
|
||
SUBA.L A2,A2 ; ...no left node buffer
|
||
SUBA.L A3,A3 ; ...no right node buffer
|
||
|
||
CLR.L BTCNodeM(A4) ; invalidate current node mark
|
||
;
|
||
; build BTree record consisting of input key with a junk data record
|
||
;
|
||
MOVEA.L BTCVarPtr(A4),A1 ; dest = record buffer
|
||
LEA BTVRecord(A1),A1 ;
|
||
MOVEA.L D5,A0 ; source = input key
|
||
MOVEQ #0,D0 ; get key length
|
||
MOVE.B (A0),D0 ;
|
||
ADDQ.L #2,D0 ; include length byte
|
||
LSR.L #1,D0 ;
|
||
LSL.L #1,D0 ; ...and adjust to word boundry
|
||
ADD.W D0,D6 ; total size = key size + data size
|
||
_BlockMove ; move in the key
|
||
|
||
ADDQ.W #1,D6 ; adjust total size to word boundry
|
||
LSR.W #1,D6 ;
|
||
LSL.W #1,D6 ;
|
||
MOVE.L A1,D7 ; D7 = ptr(Btree record)
|
||
;
|
||
; search tree for key
|
||
;
|
||
BISearch
|
||
JSRRom romTreeSearch ; search for key
|
||
MOVEA.L A1,A3 ; A3 = ptr(right node buffer)
|
||
BNE.S @1 ; key not found or error ->
|
||
MOVEQ #BTexists,D0 ; error, it already exists <14Oct85>
|
||
BRA BIExit1 ; exit ->
|
||
@1 CMP.W #BTnotfound,D0 ; key not found ?
|
||
BNE BIExit1 ; no, must be IO error ->
|
||
MOVE.L D2,D3 ; D3 = right node number
|
||
MOVE.W D1,D5 ; D5 = insert index
|
||
;
|
||
; if tree is empty, add 1st leaf node
|
||
;
|
||
TST.W BTCLevel(A4) ; tree empty ?
|
||
BGT.S BIInsert ; no ->
|
||
|
||
MOVE.W BTCRefNum(A4),D0 ; file refnum
|
||
JSR AllocNode ; allocate the node
|
||
BNE BIExit1 ; error ->
|
||
|
||
MOVE.L D1,D3 ; D3 = new node number
|
||
|
||
JSRRom romInitNode ; get an initialized node
|
||
BNE BIExit1 ; error -> <18Dec85>
|
||
MOVEA.L A0,A3 ; A3 = ptr(new root node)
|
||
|
||
MOVE.B #NDLeafNode,NDType(A3) ; type = leaf node
|
||
MOVEQ #1,D0 ; node height = 1
|
||
MOVE.B D0,NDNHeight(A3) ;
|
||
|
||
MOVE.W D0,BTCDepth(A4) ; tree depth = 1
|
||
MOVE.L D3,BTCRoot(A4) ; new root node number
|
||
|
||
MOVE.L D3,BTCFNode(A4) ; 1st and last node pointers
|
||
MOVE.L D3,BTCLNode(A4) ;
|
||
|
||
MOVE.W #1,BTCLevel(A4) ; current level = 1
|
||
;
|
||
; try a simple insert first
|
||
;
|
||
BIInsert
|
||
MOVEA.L D7,A0 ; ptr(record)
|
||
MOVE.W D6,D0 ; record size
|
||
MOVEA.L A3,A1 ; ptr(node buffer)
|
||
MOVE.W D5,D1 ; insert index
|
||
JSRRom romInsertRec ; try to insert it
|
||
BNE.S BIRotate ; didn't make it ->
|
||
|
||
TST.W D5 ; insert a new 1st record ?
|
||
BNE.S BIInsDone ; no, insert complete ->
|
||
BSET #BTCKeyUpd,BTCFlags(A4) ; indicate key update required
|
||
BRA.S BIInsDone ; insert complete ->
|
||
;
|
||
; insert didn't work, try rotation into left sibling node
|
||
;
|
||
BIRotate
|
||
MOVEA.L A3,A0 ; ptr(node buffer)
|
||
JSRRom romGetLtSib ; get left sibling node
|
||
BNE BIExit1 ; error ->
|
||
TST.L D1 ; is there a left sibling ?
|
||
BEQ.S BISplit ; no ->
|
||
MOVE.L D1,D2 ; D2 = left node number
|
||
MOVEA.L A1,A2 ; A2 = ptr(left node buffer)
|
||
|
||
MOVEA.L D7,A0 ; ptr(record)
|
||
MOVE.W D6,D0 ; record size
|
||
MOVE.W D5,D1 ; insert index
|
||
JSRRom romRotateLt ; try rotating left
|
||
BNE.S BISplit ; didn't make it ->
|
||
BRA.S BISplit1 ; use common code -> <10Oct85>
|
||
;
|
||
; rotate didn't work, got to split
|
||
;
|
||
BISplit MOVEA.L D7,A0 ; ptr(record)
|
||
MOVE.W D6,D0 ; record size
|
||
MOVE.W D5,D1 ; insert index
|
||
JSRRom romSplitLt ; split left
|
||
BEQ.S @1 ; ok ->
|
||
MOVEQ #BTnoFit,D0 ; result = 'no fit' <14Oct85>
|
||
BRA BIExit1 ; exit ->
|
||
|
||
@1 BSET #BTCNewIRec,BTCFlags(A4) ; indicate new index record required <10Oct85>
|
||
|
||
BISplit1 ;<10Oct85>
|
||
BSET #BTCKeyUpd,BTCFlags(A4) ; indicate key update required <10Oct85>
|
||
|
||
MOVE.W D1,D5 ; D5 = new record index <10Oct85>
|
||
|
||
MOVEA.L A2,A0 ; mark the left node dirty <10Oct85>
|
||
JSR MarkBlock ; <10Oct85>
|
||
;
|
||
; completed the record insert, clean up
|
||
;
|
||
BIInsDone
|
||
;; if we get to here the file has changed, even we exit with error from
|
||
;; here on. <8/16/89 KSCT>
|
||
ADDQ.L #1,BTCWcount(A4) ; bump write count <8/16/89 KSCT>
|
||
|
||
MOVEA.L A3,A0 ; mark the right node dirty <10Oct85>
|
||
JSR MarkBlock ; <10Oct85>
|
||
CMPI.B #NDleafNode,NDType(A1) ; leaf level?
|
||
BNE.S BICkDone ; no ->
|
||
|
||
MOVEM.L (A6),A0/D0 ; get input record ptr and size
|
||
JSRRom romUpdDRec ; replace the junk data record
|
||
|
||
ADD.L #1,BTCNRecs(A4) ; bump leaf record count
|
||
|
||
MOVE.W D5,BTCIndexM(A4) ; save index marker
|
||
MOVE.L D3,D0 ; assume right node number
|
||
CMPA.L A3,A1 ; is new record in right node?
|
||
BEQ.S @1 ; yes ->
|
||
MOVE.L D2,D0 ; no, use left node number
|
||
@1 MOVE.L D0,BTCNodeM(A4) ; save node marker
|
||
;
|
||
; Check if all done
|
||
;
|
||
BICkDone
|
||
BTST #BTCKeyUpd,BTCFlags(A4) ; key update required ?
|
||
BNE.S BINxtLev ; yes, move to next level ->
|
||
BTST #BTCNewIRec,BTCFlags(A4) ; new index record required ?
|
||
BEQ BIExit ; no, all done ->
|
||
;
|
||
; move to next level
|
||
;
|
||
BINxtLev
|
||
SUB.W #1,BTCLevel(A4) ; bump to next level up
|
||
BEQ BIRoot ; up to root level ->
|
||
|
||
MOVE.W BTCLevel(A4),D0 ; locate TPR
|
||
JSRRom romLocTPR ; for this level
|
||
MOVE.W TPRRIndx(A0),D5 ; D5 = parent record index
|
||
MOVE.L TPRNodeN(A0),D3 ; D3 = parent node number
|
||
|
||
MOVE.L D2,-(A6) ; save D2
|
||
MOVEQ #0,D1 ; get parent node <10Oct85>
|
||
MOVE.L D3,D2 ;
|
||
JSR GetNode
|
||
BNE BIExit1 ; error ->
|
||
MOVE.L A0,D4 ; D4 = ptr(parent node buffer)
|
||
MOVE.L (A6)+,D2 ; restore D2
|
||
;
|
||
; update parent key
|
||
;
|
||
BIUpdKey
|
||
BTST #BTCKeyUpd,BTCFlags(A4) ; key update required ?
|
||
BEQ.S @1 ; no ->
|
||
|
||
MOVE.W D5,D0 ; locate
|
||
MOVEA.L D4,A1 ;
|
||
JSRRom romGetRecA ; ...parent index record
|
||
MOVE.L A0,-(SP) ; ...and save it
|
||
MOVEA.L A3,A1 ; locate 1st key
|
||
CLR.W D0 ;
|
||
JSRRom romGetRecA ; ...in right node
|
||
MOVEA.L (SP)+,A1 ; ptr(parent index record)
|
||
|
||
JSRRom romUpdIKey ; update the key
|
||
|
||
MOVEA.L D4,A0 ; mark the parent node dirty <10Oct85>
|
||
JSR MarkBlock ; <10Oct85>
|
||
TST.W D5 ; update to 1st key in node ?
|
||
BEQ.S @1 ; yes, leave flag set ->
|
||
BCLR #BTCKeyUpd,BTCFlags(A4) ; don't need to update next level
|
||
|
||
@1 MOVEQ #0,D1 ; release <10Oct85>
|
||
MOVEA.L A3,A0 ;
|
||
JSR RelNode ; ... right node buffer
|
||
MOVEA.L D4,A3 ; parent is new right node
|
||
MOVEQ #0,D4 ; indicate no parent node buffer <29Aug85>
|
||
;
|
||
; build new index record pointing at new left node
|
||
;
|
||
BINewRec
|
||
BTST #BTCNewIRec,BTCFlags(A4) ; new index record required ?
|
||
BEQ.S @1 ; no ->
|
||
|
||
MOVEA.L A2,A1 ; left node
|
||
MOVE.L D2,D0 ; left node number
|
||
JSRRom romBuildIRec ; build index record
|
||
|
||
@1 MOVEQ #0,D1 ; release <10Oct85>
|
||
MOVEA.L A2,A0 ;
|
||
JSR RelNode ; ... left node buffer
|
||
SUBA.L A2,A2 ; indicate no left node
|
||
;
|
||
; set up for insert of new index record
|
||
;
|
||
BTST #BTCNewIRec,BTCFlags(A4) ; new index record required ?
|
||
BEQ BICkDone ; no, check if done ->
|
||
|
||
MOVEA.L BTCVarPtr(A4),A0 ; locate new index record
|
||
LEA BTVRecord(A0),A0 ;
|
||
MOVE.L A0,D7 ; D7 = ptr to new record
|
||
jsrRom romGetMaxKey ; max key length + 4 <10Oct85>
|
||
ADDQ.W #4,D0 ; = size of index record <10Oct85>
|
||
MOVE.W D0,D6 ; D6 = size of new record <10Oct85>
|
||
BCLR #BTCNewIRec,BTCFlags(A4) ; clear new index rec flag
|
||
BRA BIInsert ; insert new index record ->
|
||
;
|
||
; we updated the root, must add a level if it was split
|
||
;
|
||
BIRoot
|
||
BTST #BTCNewIRec,BTCFlags(A4) ; was root node split ?
|
||
BEQ BIExit ; no, all done ->
|
||
;
|
||
; set up a new root index node
|
||
;
|
||
MOVE.W BTCRefNum(A4),D0 ; volume refnum
|
||
JSR AllocNode ; allocate the node
|
||
BNE BIExit1 ; error ->
|
||
|
||
MOVE.L D1,D3 ; D3 = new root node number
|
||
|
||
MOVE.L D3,D1 ; new node number
|
||
jsrRom romInitNode ; get an initialized node
|
||
BNE.S BIExit1 ; error -> <18Dec85>
|
||
MOVE.L A0,D4 ; D4 = ptr(new root node buffer)
|
||
|
||
MOVE.B #NDIndxNode,NDType(A0) ; type = index node
|
||
MOVEQ #1,D0 ; node height
|
||
ADD.W BTCDepth(A4),D0 ; = previous depth +1
|
||
MOVE.B D0,NDNHeight(A0) ;
|
||
;
|
||
; add index record for left node
|
||
;
|
||
MOVEA.L A2,A1 ; build index record
|
||
MOVE.L D2,D0 ;
|
||
jsrRom romBuildIRec ; ...for left node
|
||
|
||
jsrRom romGetMaxKey ; max key length + 4 <10Oct85>
|
||
ADDQ.W #4,D0 ; = size of index record <10Oct85>
|
||
MOVEA.L BTCVarPtr(A4),A0 ; locate new index record
|
||
LEA BTVRecord(A0),A0 ;
|
||
MOVEA.L D4,A1 ; ptr(root node buffer)
|
||
CLR.W D1 ; insert index = 0
|
||
jsrRom romInsertRec ; insert it
|
||
;
|
||
; add index record for right node (previous root)
|
||
;
|
||
MOVE.W #1,D0 ; get node number
|
||
jsrRom romLocTPR ;
|
||
MOVE.L TPRNodeN(A0),D0 ; ...of previous root
|
||
MOVEA.L A3,A1 ; ptr(node buffer)
|
||
jsrRom romBuildIRec ; build index record
|
||
|
||
jsrRom romGetMaxKey ; max key length + 4 <10Oct85>
|
||
ADDQ.W #4,D0 ; = size of index record <10Oct85>
|
||
MOVEA.L BTCVarPtr(A4),A0 ; locate new index record
|
||
LEA BTVRecord(A0),A0 ;
|
||
MOVEA.L D4,A1 ; ptr(root node buffer)
|
||
MOVE.W #1,D1 ; insert index = 1
|
||
jsrRom romInsertRec ; insert it
|
||
|
||
MOVEA.L D4,A0 ; mark the new root node dirty <10Oct85>
|
||
JSR MarkBlock ; <10Oct85>
|
||
; update BTCB
|
||
;
|
||
ADD.W #1,BTCDepth(A4) ; add a level
|
||
MOVE.L D3,BTCRoot(A4) ; new root node number
|
||
;
|
||
; clean up and exit
|
||
;
|
||
BIExit
|
||
BSET #BTCDirty,BTCFlags(A4) ; mark BTCB dirty
|
||
CLR.W D0 ; result = ok
|
||
BIExit1
|
||
MOVE.W D0,-(A6) ; save result code
|
||
|
||
MOVEA.L A2,A0 ; ptr(left node buffer) <23Sep85>
|
||
MOVEQ #0,D1 ; no RelBlock options <10Oct85>
|
||
JSR RelNode ; release it
|
||
MOVEA.L A3,A0 ; ptr(right node buffer) <23Sep85>
|
||
JSR RelNode ; release it
|
||
MOVEA.L D4,A0 ; ptr(parent node buffer) <23Sep85>
|
||
JSR RelNode ; release it
|
||
MOVE.W (A6)+,D0 ; restore result code
|
||
|
||
MOVE.L BTCNodeM(A4),D2 ; return hint (node number)
|
||
CLR.L BTCNodeM(A4) ; invalidate current record markers
|
||
|
||
jsrRom romBTCleanUP ; clean up
|
||
|
||
ADDQ.L #8,A6 ; deallocate saved stuff
|
||
BIExit2
|
||
MOVEM.L (A6)+,D1/D3-D7/A0-A4 ; restore regs
|
||
|
||
MOVE.L (A6)+,-(SP) ; put return address back on stack
|
||
TST.W D0 ; set condition codes
|
||
RTS ; exit BTInsert
|
||
;__________________________________________________________________________________
|
||
; Routine: BTDelete
|
||
;
|
||
; Function: Increment the Write Count if B*Tree has changed
|
||
; <27Mar91> KST Fixing a bug that uses MOVE.W to move a long word.
|
||
;__________________________________________________________________________________
|
||
FreeNode MOVE.L jFreeNode,-(SP)
|
||
RTS
|
||
|
||
BTDeletePatch
|
||
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
|
||
|
||
MOVEM.L D1-D5/A0-A4,-(A6) ; save regs
|
||
;
|
||
; initialize some things
|
||
;
|
||
jsrRom romBTSetUp ; set up common stuff
|
||
|
||
CLR.L BTCNodeM(A4) ; invalidate current node mark
|
||
|
||
SUBA.L A2,A2 ; ...no left node buffer
|
||
SUBA.L A3,A3 ; ...no right node buffer
|
||
;
|
||
; search tree for key
|
||
;
|
||
jsrRom romTreeSearch ; search for key
|
||
MOVEA.L A1,A3 ; A3 = ptr(node buffer)
|
||
BNE BDExit1 ; didn't find it ->
|
||
MOVE.W D1,D5 ; D5 = record index
|
||
;
|
||
; delete record from node
|
||
;
|
||
BDDelete
|
||
MOVEA.L A3,A1 ; ptr(node buffer)
|
||
MOVE.W D5,D1 ; record index
|
||
jsrRom romDeleteRec ; delete it
|
||
|
||
;; No error checking here, so I assume the file has changed. <8/16/89 KSCT>
|
||
ADDQ.L #1,BTCWcount(A4) ; bump write count <8/16/89 KSCT>
|
||
|
||
MOVEA.L A3,A0 ; ptr(node buffer) <10Oct85>
|
||
JSR MarkBlock ; mark it dirty <10Oct85>
|
||
TST.W D5 ; delete 1st record ?
|
||
BNE.S @1 ; no ->
|
||
BSET #BTCKeyUpd,BTCFlags(A4) ; indicate key update required
|
||
|
||
@1 CMPI.B #NDLeafNode,NDType(A3) ; at leaf level?
|
||
BNE.S BDRemove ; no ->
|
||
SUB.L #1,BTCNRecs(A4) ; decrement number of leaf records
|
||
|
||
;
|
||
; unlink the node if the last record was deleted
|
||
;
|
||
BDRemove
|
||
TST.W NDNRecs(A3) ; node empty ?
|
||
BGT BDNxtLev ; no ->
|
||
|
||
MOVEA.L A3,A0 ; ptr(node buffer)
|
||
jsrRom romGetLtSib ; get left sibling node
|
||
BNE BDExit1 ; error ->
|
||
TST.L D1 ; is there a left sibling ?
|
||
BEQ.S @1 ; no ->
|
||
|
||
MOVE.L NDFlink(A3),NDFlink(A1) ; update forward link
|
||
MOVEQ #kRBdirty,D1 ; release <10Oct85>
|
||
MOVEA.L A1,A0 ;
|
||
JSR RelNode ; ...left node buffer
|
||
|
||
@1 MOVEA.L A3,A0 ; ptr(node buffer)
|
||
jsrRom romGetRtSib ; get right sibling node
|
||
BNE BDExit1 ; error ->
|
||
TST.L D1 ; is there a right sibling ?
|
||
BEQ.S BDUpdPtrs ; no ->
|
||
|
||
MOVE.L NDBlink(A3),NDBlink(A1) ; update backward link
|
||
MOVEQ #kRBdirty,D1 ; release <10Oct85>
|
||
MOVEA.L A1,A0 ;
|
||
JSR RelNode ; ...right node buffer
|
||
;
|
||
; update 1st and last node pointers
|
||
;
|
||
BDUpdPtrs
|
||
CMPI.B #NDLeafNode,NDType(A3) ; at leaf level?
|
||
BNE.S BDFree ; no ->
|
||
|
||
TST.L NDBLink(A3) ; backward link = 0?
|
||
BNE.S @1 ; no ->
|
||
MOVE.L NDFlink(A3),BTCFNode(A4) ; yes, update 1st node number
|
||
|
||
@1 TST.L NDFLink(A3) ; forward link = 0?
|
||
BNE.S BDFree ; no ->
|
||
MOVE.L NDBlink(A3),BTCLNode(A4) ; yes, update last node number
|
||
;
|
||
; free the empty node
|
||
;
|
||
BDFree
|
||
MOVEA.L A3,A0 ; clear the empty node <23Sep85>
|
||
jsrRom romClrNode ; <23Sep85>
|
||
|
||
MOVEQ #kRBdirty,D1 ; release <10Oct85>
|
||
JSR RelNode ; ...node buffer
|
||
SUBA.L A3,A3 ; indicate no buffer
|
||
|
||
MOVE.W BTCLevel(A4),D0 ; locate
|
||
jsrRom romLocTPR ; ...TPR
|
||
|
||
MOVE.L TPRNodeN(A0),D1 ; free
|
||
MOVE.W BTCRefNum(A4),D0 ;
|
||
JSR FreeNode ; ...the disk node
|
||
BSET #BTCDelIRec,BTCFlags(A4) ; indicate index record delete
|
||
BCLR #BTCKeyUpd,BTCFlags(A4) ; ...and no key update
|
||
;
|
||
; move to next level
|
||
;
|
||
BDNxtLev
|
||
SUB.W #1,BTCLevel(A4) ; bump to next level up
|
||
BEQ BDRoot ; up to root level ->
|
||
|
||
BTST #BTCKeyUpd,BTCFlags(A4) ; key update required ? <03Sep85>
|
||
BNE.S @1 ; yes -> <03Sep85>
|
||
BTST #BTCDelIRec,BTCFlags(A4) ; index record delete required ? <03Sep85>
|
||
BEQ BDExit ; no, all done -> <03Sep85>
|
||
|
||
@1 MOVE.W BTCLevel(A4),D0 ; locate TPR
|
||
jsrRom romLocTPR ; for this level
|
||
MOVE.W TPRRIndx(A0),D5 ; D5 = parent record index
|
||
MOVE.L TPRNodeN(A0),D2 ; D2 = parent node number
|
||
|
||
MOVEQ #0,D1 ; get parent node <10Oct85>
|
||
JSR GetNode
|
||
BNE BDExit1 ; error -> <20Nov85>
|
||
MOVEA.L A0,A2 ; A2 = ptr(parent node buffer)
|
||
;
|
||
; update parent key
|
||
;
|
||
BDUpdKey
|
||
BTST #BTCKeyUpd,BTCFlags(A4) ; key update required ?
|
||
BEQ.S @1 ; no ->
|
||
|
||
MOVE.W D5,D0 ; locate
|
||
MOVEA.L A2,A1 ;
|
||
jsrRom romGetRecA ; ...parent index record
|
||
MOVE.L A0,-(SP) ; ...and save it
|
||
MOVE.L A3,A1 ; locate 1st key
|
||
CLR.W D0 ;
|
||
jsrRom romGetRecA ; ...in node
|
||
MOVEA.L (SP)+,A1 ; ptr(parent index record)
|
||
|
||
jsrRom romUpdIKey ; update the key
|
||
|
||
MOVEA.L A2,A0 ; ptr(parent node buffer) <10Oct85>
|
||
JSR MarkBlock ; mark it dirty <10Oct85>
|
||
TST.W D5 ; update to 1st key in node ?
|
||
BEQ.S @1 ; yes, leave flag set ->
|
||
BCLR #BTCKeyUpd,BTCFlags(A4) ; don't need to update next level
|
||
|
||
@1 MOVEQ #0,D1 ; release child <10Oct85>
|
||
MOVE.L A3,A0 ;
|
||
JSR RelNode ; ...node buffer
|
||
MOVEA.L A2,A3 ; new child node = parent node
|
||
MOVE.L D2,D3 ; preserve parent node number <27Mar91 #50>
|
||
; MOVE.L please, not .W <27Mar91 #50>
|
||
;
|
||
; check if done
|
||
;
|
||
BCLR #BTCDelIRec,BTCFlags(A4) ; indx rec delete required ? <03Sep85>
|
||
BEQ.S @2 ; no ->
|
||
BRA BDDelete ; delete parent index record ->
|
||
|
||
@2 BTST #BTCKeyUpd,BTCFlags(A4) ; another key update required ?
|
||
BNE BDNxtLev ; yes ->
|
||
BRA.S BDExit ; no, all done ->
|
||
;
|
||
; up to the root, check for an empty tree
|
||
;
|
||
BDRoot
|
||
MOVE.L A3,D0 ; root node released?
|
||
BNE.S BDUpdDepth ; no ->
|
||
CLR.W BTCDepth(A4) ; set tree to empty state
|
||
CLR.L BTCRoot(A4) ;
|
||
BRA.S BDExit ; exit ->
|
||
;
|
||
; update tree depth
|
||
;
|
||
BDUpdDepth
|
||
CMPI.W #1,NDNRecs(A3) ; check # of records left in root
|
||
BGT.S BDExit ; > 1, all done ->
|
||
TST.B NDType(A3) ; index node ?
|
||
BNE.S BDExit ; no, all done ->
|
||
|
||
SUBQ.W #1,BTCDepth(A4) ; decrement tree depth
|
||
|
||
MOVEQ #0,D1 ; locate key and data for 1st record <10Oct85>
|
||
MOVEA.L A3,A1 ;
|
||
jsrRom LOCREC ; ... in node <10Oct85>
|
||
MOVE.L (A1),BTCRoot(A4) ; new root = child node <10Oct85>
|
||
;
|
||
; release the previous root node
|
||
;
|
||
MOVEA.L A3,A0 ; clear the node buffer <23Sep85>
|
||
jsrRom romClrNode ; <23Sep85>
|
||
|
||
MOVEQ #kRBdirty,D1 ; release the <10Oct85>
|
||
JSR RelNode ; ...node buffer
|
||
SUBA.L A3,A3 ; indicate no buffer
|
||
|
||
MOVE.L D3,D1 ; free
|
||
MOVE.W BTCRefNum(A4),D0 ;
|
||
JSR FreeNode ; ...the node
|
||
; <20Nov85>
|
||
; get new root node and check if depth can be reduced again <20Nov85>
|
||
; <20Nov85>
|
||
MOVE.L BTCRoot(A4),D3 ; D3 = new root node # <20Nov85>
|
||
MOVE.L D3,D2 ; get the new root node <20Nov85>
|
||
MOVEQ #0,D1 ; <20Nov85>
|
||
JSR GetNode ; <20Nov85>
|
||
BNE.S BDExit1 ; error -> <20Nov85>
|
||
|
||
MOVEA.L A0,A3 ; A3 = ptr to node buffer <20Nov85>
|
||
BRA.S BDUpdDepth ; check out new root -> <20Nov85>
|
||
|
||
;
|
||
; release last node and exit
|
||
;
|
||
BDExit
|
||
BSET #BTCDirty,BTCFlags(A4) ; mark BTCB dirty
|
||
CLR.W D0 ; result = ok
|
||
BDExit1
|
||
MOVE.W D0,-(A6) ; save result code
|
||
|
||
MOVEA.L A3,A0 ; ptr(node buffer) <23Sep85>
|
||
MOVEQ #0,D1 ; <10Oct85>
|
||
JSR RelNode ; release it
|
||
MOVE.W (A6)+,D0 ; restore result code
|
||
|
||
jsrRom romBTCleanUP ; clean up
|
||
|
||
MOVEM.L (A6)+,D1-D5/A0-A4 ; restore regs <29Aug85>
|
||
|
||
MOVE.L (A6)+,-(SP) ; put return address back on stack
|
||
TST.W D0 ; set condition codes
|
||
RTS ; exit BTDelete
|
||
|
||
endp
|
||
; End of Catalog Btree writeCount patch
|
||
|
||
;________________________________________________________________________________
|
||
;
|
||
; Routine: CheckDesktopSupport
|
||
;
|
||
; Input: a2 - pointer to local volume's vcb
|
||
; Output: zero flag clear if we support the desktop on this volume
|
||
; zero flag set if we don't
|
||
;
|
||
; Function: Indicate whether a volume can support the desktop manager calls
|
||
;
|
||
; Only call this on local hfs volumes
|
||
;
|
||
; The rule: A local volume gets desktop support if it is either
|
||
; 1) non-ejectable
|
||
; 2) ejectable but bigger than DTMinVolSize
|
||
;
|
||
; AlkBlkSiz is stored as a long and used as a word throughout HFS
|
||
;________________________________________________________________________________
|
||
; rolled into TFS.a for SuperMario <SM1> FM
|
||
|
||
CDSRegs reg d0/d1/a1
|
||
CheckDesktopSupport: proc
|
||
|
||
movem.l CDSRegs, -(sp)
|
||
|
||
;
|
||
; Is this an MFS volume?
|
||
;
|
||
cmp.w #SigWord,vcbSigWord(a2) ; is this an MFS volume?
|
||
beq.s @NoSupport ; yes, so use the exciting resource file technique
|
||
;
|
||
; Is this volume bigger than DTMinVolSize?
|
||
;
|
||
move.w vcbNmBlks(a2), d0 ; d0 = # of allocation blocks on this volume
|
||
move.w vcbAlBlkSiz+2(a2), d1 ; d1 = allocation block size
|
||
; AlkBlkSiz is used as a word throughout HFS
|
||
mulu.w d1, d0 ; d0 = # bytes on this volume
|
||
cmp.l #DTMinVolSize, d0 ; is this a big volume?
|
||
bhs.s @SupportsDT ; then we always support the DTDB
|
||
;
|
||
; It's not a big volume, but check and see if the volume is ejectable
|
||
;
|
||
move.l DrvQHdr+qHead, a1 ; a1 = ptr (1st drive in Drive Queue)
|
||
|
||
move.w vcbDrvNum(a2), d0 ; d0 = drive number for this volume (if online)
|
||
bne.s @SearchDrvQ ; if it's online, we can go straight to the search
|
||
|
||
move.w vcbDRefNum(a2), d1 ; d1 = + or - DrvNum (since we know we're not online)
|
||
cmp.w d0, d1 ; = means volume is ejected
|
||
beq.s @NoSupport ; which means it gets no DT support
|
||
move.w d1, d0 ; d0 now = -DrvNum (since we know it's offline)
|
||
neg.w d0 ; d0 now = DrvNum, so we can search the drive Q
|
||
;
|
||
; We now have the drive number in d0, and we know that the drive is not big enough to
|
||
; automatically get DT support. We need to see if it is ejectable, and if it is a small
|
||
; non-ejectable volume (say, a ramdisk) we support it.
|
||
;
|
||
@SearchDrvQ:
|
||
cmp.w dQDrive(a1), d0 ; is this the dQ entry we want?
|
||
beq.s @GotDQ
|
||
move.l qLink(a1), d1 ; get the next dQ entry (test for zero)
|
||
movea.l d1, a1 ; move to an A reg where we can use it
|
||
bne.s @SearchDrvQ ; and keep going if there is one
|
||
move.w #nsvErr, d0 ; if we have a real vcb, we should find a dQ entry
|
||
bra.s @NoSupport ; but if we ever don't, let's return 'no support'
|
||
|
||
@GotDQ:
|
||
cmp.b #8, -3(a1) ; drive queue entry flag bytes => 8 (signed) are non-ejectable <19><51>
|
||
blt.s @NoSupport ; we don't support DT on small ejectable volumes
|
||
|
||
@SupportsDT:
|
||
moveq.l #1, d0 ; clear the zero flag
|
||
bra.s @CDSExit
|
||
|
||
@NoSupport:
|
||
moveq.l #0, d0 ; set the zero flag
|
||
@CDSExit: movem.l (sp)+, CDSRegs
|
||
rts
|
||
|
||
;________________________________________________________________________________
|
||
;
|
||
; Routine: GetVolParms
|
||
;
|
||
; Input: a0 - pointer to user's param block
|
||
; Output: d0 - result code
|
||
;
|
||
; Function: Return a version 2 GetVolParms param block
|
||
;
|
||
; Interesting problems with GetVolParms (which aren't important enough to cause
|
||
; us to make a change this close to 7.0 final. Obviously, if you're reading this
|
||
; after 7.0 final, please make these changes for the good of humanity. You should find
|
||
; these written up in the BRC).
|
||
; 1) The bclr #asyncTrpBit,ioTrap(a0) instruction is old cruft. The two problems with
|
||
; the line are that (a) forcing GetVolParms calls synchronous will hang interrupt-time
|
||
; callers, and (b) the line doesn't even do what it says. The trap word is in d1
|
||
; before the call to FSQueue. ioTrap is about have d1 stored in it. If you are possessed
|
||
; of the urge to put your initials into the system, perhaps you could replace the bclr
|
||
; with a store of 16 bits of your own choosing.
|
||
; 2) The suba.w @VolParmsBufferSize,a6 instruction needs to be changed to a long subtract.
|
||
; What happens now is that zero is subtracted from a6, but the size of the saved registers
|
||
; happens to be bigger than the buffer, so there's enough room on the stack. Since the File
|
||
; Manager happens to save registers before dispatching, the fact that GetVolParms trashes
|
||
; the registers is unimportant. Thus, the three parallel changes are to get rid of the
|
||
; regster save/restore, change the suba.w to a suba.l, and add an adda.l at the bottom of
|
||
; the routine to deallocate the buffer.
|
||
;
|
||
;________________________________________________________________________________
|
||
;
|
||
GetVolParms: proc export
|
||
@GetVolRegs reg d2/d3/a1-a4
|
||
|
||
bclr #asyncTrpBit, ioTrap(a0) ; force synchronous (wrong. See note, above)
|
||
jsrRom FSQUEUE ; Patiently wait our turn in line
|
||
movem.l @GetVolRegs, -(a6) ; save regs on FS stack
|
||
suba.w @VolParmsBufferSize, a6 ; allocate a GetVolParms buffer on the stack
|
||
;
|
||
; Find the VCB in question:
|
||
;
|
||
jsrRom DTRMV3 ; find vol using ioNamePtr & ioVRefNum (D023/A234 trashed)
|
||
bne.s @GetVolParmsExit ; (DtrmV3 puts VCBPtr in A2)
|
||
|
||
tst.w vcbFSID(a2) ; Check: external file system? (•• TFSVCBTst?)
|
||
beq.s @ForUs ; If zero, it's HFS.
|
||
;
|
||
; This call is for an External File System volume:
|
||
;
|
||
moveq #extFSErr, d0 ; Tell the world
|
||
bra.s @GetVolParmsExit ; and let them worry about it
|
||
;
|
||
; This call is for us. First, make a modifiable copy of the return buffer.
|
||
;
|
||
@ForUs:
|
||
movea.l a0, a4 ; move user's pb to a safe register
|
||
move.l @VolParmsBufferSize, d0 ; d0 = size of GetVolParms' ioBuffer
|
||
movea.l a6, a1 ; onto our stack buffer
|
||
lea.l @HFSVolParmsBuffer, a0 ; copy the static values for HFS volumes
|
||
cmp.w #SigWord,vcbSigWord(a2) ; is this an MFS volume?
|
||
bne.s @notMFS ; no, skip the following
|
||
lea.l @MFSVolParmsBuffer, a0 ; copy the static values for MFS volumes
|
||
@notMFS:
|
||
_BlockMove
|
||
|
||
bsr CheckDesktopSupport ; returns zero clear if support, set if none
|
||
beq.s @NoDTSupport ; z set means no support
|
||
|
||
move.l vmAttrib(a6), d0 ; get the attributes word
|
||
bset.l #bHasDesktopMgr, d0 ; set the bit indicating support
|
||
move.l d0, vmAttrib(a6) ; put the modified word back in the buffer
|
||
@noDTSupport:
|
||
|
||
;
|
||
; The buffer on the stack reflects the correct values for this volume.
|
||
; Copy back as much of it as the caller gave us room.
|
||
;
|
||
move.l ioReqCount(a4), d0 ; get user's buffer size
|
||
cmp.l @VolParmsBufferSize, d0 ; is it smaller than our buffer?
|
||
bls.s @NoClip ; then just return that amount
|
||
|
||
move.l @VolParmsBufferSize, d0 ; clip the return to just the size of our data
|
||
|
||
@noClip:
|
||
move.l d0, ioActCount(a4) ; tell'em how much we're copying
|
||
move.l a6, a0 ; copy from our stack buffer
|
||
move.l ioBuffer(a4), a1 ; copy into the user's pb buffer
|
||
_BlockMove
|
||
|
||
@GetVolParmsExit:
|
||
movem.l (a6)+, @GetVolRegs ; restore regs
|
||
jmpROM CMDDONE ; Go home
|
||
|
||
; This buffer contains the appropriate values for local HFS volumes.
|
||
@HFSVolParmsBuffer:
|
||
dc.w 2 ; this is a version 2 getvolparms
|
||
dc.l localvMAttribs ; static set of local attribute bits
|
||
dc.l 0 ; no local window handle
|
||
dc.l 0 ; no server address
|
||
dc.l localVolGrade ; standard volume grade
|
||
dc.w 0 ; local volumes don't support alt priv models
|
||
@VolParmsBufferSize:
|
||
dc.l *-@HFSVolParmsBuffer
|
||
|
||
; This buffer contains the appropriate values for local MFS volumes.
|
||
@MFSVolParmsBuffer:
|
||
dc.w 2 ; this is a version 2 getvolparms
|
||
dc.l 0 ; no optional calls supported for MFS volumes
|
||
dc.l 0 ; no local window handle
|
||
dc.l 0 ; no server address
|
||
dc.l localVolGrade ; standard volume grade
|
||
dc.w 0 ; local volumes don't support alt priv models
|
||
endproc
|
||
|
||
;________________________________________________________________________________
|
||
;
|
||
; Desktop Manager and Queue Manager initialization
|
||
;________________________________________________________________________________
|
||
|
||
; FSVars has been allocated by btree patches. Allocate the DTDBMgr's globals block
|
||
DTDBMgrInit proc export
|
||
|
||
move.l #DTGlobals.size, d0 ; we need this much space for DT manager
|
||
_NewPtr sys, clear
|
||
bne @fail ; no? Run away.
|
||
move.l FSVarsPtr, a1 ; a1 = ptr(FSVars block)
|
||
move.l a0, FSVars.DTDBMgr(a1) ; stuff ourselves into our slot in FSVars
|
||
rts
|
||
|
||
@fail
|
||
moveq.l #dsMemFullErr, d0 ; sys heap is full, so punt
|
||
_SysError
|
||
endproc
|
||
|
||
; Allocate the Queue Manager's globals block
|
||
QMInit proc export
|
||
|
||
; Allocate a block big enough of all of the Queue Manager's needs:
|
||
; Queue manager globals
|
||
; Desktop manager QMRec
|
||
; Compatibility layer QMRec
|
||
; Desktop manager stack
|
||
; Compatibility layer stack
|
||
|
||
move.l #QMGlobals.size+QMRec.size*2+DTStackSize+clStackSize, d0
|
||
_NewPtr sys, clear
|
||
bne.s @fail ; fail if we can't get memory
|
||
|
||
move.l FSVarsPtr, a1 ; a1 = ptr(FSVars block)
|
||
move.l a0, FSVars.QMgr(a1) ; leave a pointer to globals in FSVars
|
||
move.l a0, a1 ; a1 = ptr(FSVars.QMgr)
|
||
adda.l #QMGlobals.size, a0 ; skip over globals
|
||
move.l a0, QMGlobals.DTQueuePtr(a1); stuff pointer to DTMgr's QMRec
|
||
adda.l #QMRec.size, a0 ; skip over DTMgr's QMRec
|
||
move.l a0, QMGlobals.CLQueuePtr(a1); stuff pointer to Comp Layer's QMRec
|
||
|
||
adda.l #QMRec.size+DTStackSize, a0 ; skip over QMRec and Stack
|
||
move.l QMGlobals.DTQueuePtr(a1), a1 ; a1 = ptr(DTMgr's QMRec)
|
||
move.l a0, QMRec.stackBase(a1) ; store stack address (stack grows down)
|
||
move.w #desktopQType, QMRec.qType(a1) ; set queue type/refnum
|
||
|
||
adda.l #CLStackSize, a0 ; skip to end of CLStack
|
||
move.l FSVarsPtr, a1 ; a1 = ptr(FSVars)
|
||
move.l FSVars.QMgr(a1), a1 ; a1 = ptr(QM globals)
|
||
move.l QMGlobals.CLQueuePtr(a1), a1 ; a1 = ptr(Comp Layer's QMRec)
|
||
move.l a0, QMRec.stackBase(a1) ; store stack address (stack grows down)
|
||
move.w #fsCompatQType, QMRec.qType(a1) ; set queue type/refnum
|
||
|
||
rts
|
||
|
||
@fail:
|
||
moveq.l #dsMemFullErr, d0 ; sys heap is full, so punt
|
||
_SysError
|
||
endproc
|
||
|
||
|
||
; __________________________________________________________________________________
|
||
PROC ; <2.3 kst>
|
||
EXPORT WriteCountInit, FSGrowPatchInit
|
||
|
||
; Need to fix up the volumes that don't have a chance to call my BTOpenPatch !
|
||
WriteCountInit
|
||
MOVEM.L A2-A3,-(SP) ; free A2,A3
|
||
MOVEA.L FCBsPtr,A3 ; A3=FCB array
|
||
MOVE.L VCBQHdr+QHead,D0 ; search the VCB queue
|
||
BEQ.S wcEndq ; br if end of queue reached
|
||
CkVol: MOVE.L D0,A2 ; next VCB pointer
|
||
jsrRom TFSVCBTST ; is this a HFS volume?
|
||
BNE.S @6 ; No, don't bother
|
||
TST vcbFSID(A2) ; internal HFS? (do we need to test this?)
|
||
BNE.S @6 ; No, don't bother
|
||
;; OK, it's for us, replace the BTCB of the B*tree files on this volume.
|
||
;; First process the EXT file:
|
||
MOVE vcbXtRef(A2),D1 ; D1=EXT file refnum
|
||
BEQ.S @4 ; just in case
|
||
@2 MOVE.L #lenBTCB70,D0 ; allocate space for BTCB
|
||
_NewPtr sys, clear
|
||
bne.s WCErrExit ; couldn't get it ->
|
||
MOVEA.L fcbBTCBPtr(A3,D1),A1 ; get the original BTCB
|
||
MOVE.L A0,fcbBTCBPtr(A3,D1) ; replace with the new one
|
||
MOVE.L #WCSigLong,BTCWcount(A0); .. and set the write count
|
||
|
||
MOVE.L #(LenBTCB/2-1),D0 ; length of BTCB - 1
|
||
MOVE.L A1,-(SP) ; save old btcb
|
||
@4 MOVE (A1)+,(A0)+ ; copy over
|
||
DBF D0,@4
|
||
MOVEA.L (SP)+,A0 ; free the old btcb
|
||
_DisposPtr
|
||
CMP vcbCtRef(A2),D1 ; is this CAT file refnum?
|
||
BEQ.S @6 ; br if it is, otherwise,
|
||
;; ... and then process the CAT file:
|
||
MOVE vcbCtRef(A2),D1 ; D1=CAT file refnum
|
||
BNE.S @2
|
||
|
||
@6 MOVE.L QLink(A2),D0 ; next VCB
|
||
BNE.S CkVol
|
||
|
||
wcEndq LeaResident BTOpenPatch,A1 ; start of BTOpen Patch code
|
||
MOVE.L A1,jBTOpen ; replace the old one ($744)
|
||
LeaResident BTInsertPatch,A1
|
||
MOVE.L A1,jBTInsert ; ($740)
|
||
LeaResident BTDeletePatch,A1
|
||
MOVE.L A1,jBTDelete ; ($734)
|
||
MOVEM.L (SP)+,A2-A3
|
||
RTS
|
||
|
||
WCErrExit:
|
||
MOVEQ #dsMemFullErr,D0 ; no mem
|
||
_SysError
|
||
|
||
;__________________________________________________________________________________
|
||
; <41>
|
||
; Enlarge the File System stack.
|
||
;
|
||
; Inputs:
|
||
; none
|
||
;
|
||
; Output:
|
||
; none
|
||
;
|
||
; Function:
|
||
; Queue up a dummy call (use the dispatch selector as if we were _FSControl)
|
||
; so we can safely replace the file system stack with a larger one. Throw
|
||
; up a bomb if we fail.
|
||
;
|
||
;__________________________________________________________________________________
|
||
; Don't need to Roll into SuperMario <SM1> fm
|
||
GrowFileSystemStack:
|
||
|
||
moveq.l #0,d0 ; use selector 0
|
||
move.w #$a060,d1 ; and pretend we're trap _HFSDispatch
|
||
jsrROM FSQUEUE ; and sync up with the file system
|
||
|
||
; first we use the old equate to get the pointer block's base address
|
||
move.l HFSStkTop,a0 ; current stack top
|
||
suba.w #HFSStkLen,a0 ; compute pointer block address
|
||
_DisposePtr ; free it up
|
||
|
||
; use the new equate to allocate a larger stack
|
||
move.l #(fsLargerStackLength+HFSTmpSize),d0 ; Size of stack area
|
||
_NewPtr sys, clear ; Allocate space in system heap
|
||
bne.s @bail ; A crushing blow if we can't get it
|
||
adda.w #fsLargerStackLength,a0 ; point to the end of the stack
|
||
move.l a0,HFSStkTop ; Store pointer in lo-mem
|
||
|
||
moveq.l #noErr,d0 ; success!
|
||
jmpROM CMDDONE
|
||
|
||
@bail:
|
||
moveq.l #dsMemFullErr, d0 ; sys heap is full, so punt
|
||
_SysError
|
||
|
||
;__________________________________________________________________________________
|
||
;
|
||
; Initialize FCB expansion patch, WDCB expansions patch, and vectors
|
||
;__________________________________________________________________________________
|
||
; Roll into SuperMario <SM1> fm
|
||
FSGrowPatchInit:
|
||
suba.w #ioHVQElSize,sp ; param blocks are de rigeur
|
||
movea.l sp,a0
|
||
bsr GrowFileSystemStack ; we need a bigger stack for 7.0 <41>
|
||
adda.w #ioHVQElSize,sp ; deallocate the pb
|
||
|
||
; allocate the FCB parallel array
|
||
movea.L FCBSPtr,a1 ; a1 = address of main FCB array
|
||
moveq #0,d0 ; clear d0 for long calc
|
||
move.w (a1),d0 ; bytes in FCB array and header
|
||
; subq #2,d0 ; remove header (doesn't affect divu)
|
||
divu.w FSFCBLen,d0 ; divide by element size to get count of FCBs
|
||
andi.l #$FFFF,d0 ; clear d0 top word set by divu
|
||
move.w d0,d1 ; Save # of FCBs in d1
|
||
lsl #L2ExtFCBLen,d0 ; d0 = total size of the parallel array
|
||
addq #fcbPBufData,d0 ; include info words
|
||
_NewPtr sys, clear ; Allocate buffer for FCBs
|
||
bne.s WCErrExit ; Any errors are fatal
|
||
|
||
; initialize parallel FCB array header and install the array in FSVars
|
||
move.w d1,cbPBufCount(a0) ; first word = count
|
||
move.w #ExtFCBLen,cbPBufULen(a0) ; second word = unit size
|
||
movea.L FSVarsPtr,a1 ; save array address in FSVars
|
||
move.l a0,FSVars.FCBPBuf(a1) ; now we're ready to record file opens!
|
||
|
||
; allocate the WDCB parallel array
|
||
move.l WDCBSPtr,a1 ; a1 = address of main WDCB array
|
||
moveq #0,d0 ; clear d0 for long calc
|
||
move.w (a1),d0 ; length of WDCB array
|
||
; subq #2,d0 ; remove header (doesn't affect divu)
|
||
divu.w #WDCBLen,d0 ; divide by element size to get count of WDCBs
|
||
andi.l #$FFFF,d0 ; clear d0 top word
|
||
move.w d0,d1 ; Save # of WDCBs in d1
|
||
lsl #L2ExtWDCBLen,d0 ; d0 = total size of the parallel array
|
||
addq #wdcbPBufData,d0 ; include info words
|
||
_NewPtr sys, clear ; Allocate buffer for FCBs
|
||
bne.s WCErrExit ; Any errors are fatal
|
||
|
||
; initialize parallel WDCB array header and install the array in FSVars
|
||
move.w d1,cbPBufCount(a0) ; first word = count
|
||
move.w #ExtWDCBLen,cbPBufULen(a0) ; second word = unit size
|
||
movea.L FSVarsPtr,a1 ; save array address in FSVars
|
||
move.l a0,FSVars.WDCBPBuf(a1) ; now we're ready to record _OpenWDs!
|
||
|
||
; initialize FCB expansion mechanism <38>
|
||
movea.l FSVarsPtr,a1
|
||
move.w #fsDesiredFCBBurst,FSVars.fsFCBBurst(a1)
|
||
move.w #fsDesiredFCBFree,FSVars.fsFCBGrow(a1)
|
||
clr.w FSVars.fsFCBCounter(a1) ; make us check on the next open
|
||
move.w #$7fff,d0 ; max signed int is all we can have for the FCB array
|
||
ext.l d0 ; divide takes a long input
|
||
divs fsFCBLen,d0 ; d0 = max # of FCBs
|
||
move.w d0,FSVars.fsFCBMax(a1)
|
||
|
||
rts
|
||
|
||
endproc
|
||
|
||
; __________________________________________________________________________________
|
||
; InstallVectors
|
||
;
|
||
; Function:
|
||
; Attach ourselves to the appropriate vectors.
|
||
;
|
||
; For fsQueueHook, ExtFSHook and jFileOpen we selfishly insist that
|
||
; we're the first piece of code hooked in so that we can assume
|
||
; that we're the last one called.
|
||
;
|
||
; __________________________________________________________________________________
|
||
; Rolled into TFS.a for SuperMario <SM1> fm
|
||
InstallVectors PROC export
|
||
|
||
; slam onto fsQueueHook, ExtFSHook and jFileOpen
|
||
|
||
LeaResident fsQueueHookPatch, a0 ; our patch address
|
||
move.l a0, fsQueueHook ; be the first ones
|
||
|
||
LeaResident ExtFSHookPatch, a0 ; our patch address <38>
|
||
move.l a0, ExtFSHook ; be the first ones <38>
|
||
|
||
LeaResident PreflightFileOpen, a0 ; our patch address <38>
|
||
move.l a0, jFileOpen ; be the first ones <38>
|
||
|
||
; install some vectors:
|
||
move.l ExpandMem, a1
|
||
leaResident vSyncWait, a0 ; get addr of syncwait (in this fsQueueHook patch)
|
||
move.l a0, ExpandMemRec.jSyncWait(a1)
|
||
leaResident vAfterFSQHook, a0 ; get addr just past call to fsQueueHook (in fsQueueHook patch)
|
||
move.l a0, ExpandMemRec.jAfterFSQHook(a1)
|
||
|
||
leaROM CMDDONE , a0 ; get addr of CmdDone (still in ROM)
|
||
move.l a0, ExpandMemRec.jCmdDone(a1)
|
||
leaResident fsGeneralWakeUp, a0 ; get addr of general purpose File System kickstart
|
||
move.l a0, ExpandMemRec.jDispatchNext(a1)
|
||
|
||
leaResident fsSCSIFreeHookProc, a0; get addr of File System SCSI Busy deferral completion
|
||
move.l a0, ExpandMemRec.jSCSIFreeHook(a1)
|
||
|
||
rts
|
||
endproc
|
||
|
||
;——————————————————————————————————————————————————————————————————————————————————————————
|
||
|
||
; Rolled into InitFS in TFS.a for SuperMario.
|
||
; <SM1> FM
|
||
;
|
||
HFS70Init InstallProc
|
||
|
||
IF BuildHFS70Init THEN ; HfsInit will allocate FSVarsPtr again and
|
||
; destroys BTVars initialized by patch
|
||
move.l FSVarsPtr,D0 ; has this been initialized?
|
||
cmp.l #-1,D0 ; $FFFF FFFF if not
|
||
bne.s @fsVarOK ; already initialized
|
||
|
||
ENDIF
|
||
|
||
move.l #FSVars.size, d0 ; d0 = size of file system variable block
|
||
_NewPtr sys, clear ; go get the block
|
||
bne @fail ; die horribly if we don't get it
|
||
move.l a0, FSVarsPtr ; stash the address in lomem
|
||
move.w #FSVars.size, FSVars.length(a0) ; store length of block in 1st word
|
||
|
||
@fsVarOK:
|
||
IF NOT BuildHFS70Init THEN ; don't allocate memory again
|
||
jsr FSGrowPatchInit ; FCBs, WDCBs and vectors
|
||
jsr QMInit ; initialize the Queue Manager
|
||
jsr DTDBMgrInit ; initialize desktop DB manager
|
||
ENDIF
|
||
jsr WriteCountInit ; for Write Count patch <2.3 kst>
|
||
jsr InstallVectors
|
||
rts
|
||
|
||
@fail:
|
||
moveq.l #dsMemFullErr, d0 ; sys heap is full, so punt
|
||
_SysError
|
||
endproc
|
||
|
||
;——————————————————————————————————————————————————————————————————————————————————————————
|
||
; <48>
|
||
; Patches to ROM btree manager routine on the Mac Plus. These routines incorrectly
|
||
; calculate the size of btree vars, returning a value 4 bytes too small. The routines
|
||
; which then use btree vars will trash the last register saved. These patches work around
|
||
; the problem by saving the register in question on the a6 stack before running the routine.
|
||
;——————————————————————————————————————————————————————————————————————————————————————————
|
||
|
||
; Plus only, No need to roll in for SuperMario!
|
||
SaveD1AcrossBTDelete PatchProc jBTDelete,(Plus)
|
||
|
||
move.l (sp)+,-(a6) ; copy return address onto the A6 stack
|
||
move.l d1,-(a6) ; save register which will be trashed
|
||
jsrOld
|
||
move.l (a6)+,d1
|
||
move.l (a6)+,-(sp) ; get back the return address
|
||
tst.w d0 ; set up condition codes again
|
||
rts
|
||
|
||
EndProc
|
||
|
||
; Plus only, No need to roll in for SuperMario!
|
||
SaveD3AcrossBTSearch PatchProc jBTSearch,(Plus)
|
||
|
||
move.l (sp)+,-(a6) ; copy return address onto the A6 stack
|
||
move.l d3,-(a6) ; save register which will be trashed
|
||
jsrOld
|
||
move.l (a6)+,d3
|
||
move.l (a6)+,-(sp) ; get back the return address
|
||
tst.w d0 ; set up condition codes again
|
||
rts
|
||
|
||
EndProc
|
||
|
||
; Plus only, No need to roll in for SuperMario!
|
||
SaveD1AcrossBTInsert PatchProc jBTInsert,(Plus)
|
||
|
||
move.l (sp)+,-(a6) ; copy return address onto the A6 stack
|
||
move.l d1,-(a6) ; save register which will be trashed
|
||
jsrOld
|
||
move.l (a6)+,d1
|
||
move.l (a6)+,-(sp) ; get back the return address
|
||
tst.w d0 ; set up condition codes again
|
||
rts
|
||
|
||
EndProc
|
||
|
||
; Plus only, No need to roll in for SuperMario!
|
||
SaveD3AcrossBTGetRecord PatchProc jBTGetRecord,(Plus)
|
||
|
||
move.l (sp)+,-(a6) ; copy return address onto the A6 stack
|
||
move.l d3,-(a6) ; save register which will be trashed
|
||
jsrOld
|
||
move.l (a6)+,d3
|
||
move.l (a6)+,-(sp) ; get back the return address
|
||
tst.w d0 ; set up condition codes again
|
||
rts
|
||
|
||
EndProc
|
||
|
||
;_______________________________________________________________________
|
||
; <49>
|
||
; MountVolFor1991 creates a need to prevent the ROM CheckRemount code from
|
||
; running, since it handles remounts, and has a stricter rule than the ROM code.
|
||
; On the Portable, II, and IIci, we can simply point the CheckRemount vector
|
||
; past the end of the CheckRemount routine. On the Plus and SE, we have
|
||
; to do a come-from patch on BTOpen and AccessBT to get the same effect.
|
||
; This patch needs to be after the earlier BTOpen patch, which is installed
|
||
; by an InstallProc towards the end of the file.
|
||
;_______________________________________________________________________
|
||
; Rolled in to TFSVol.a <SM1> FM
|
||
|
||
KillCheckRemountNiceWay InstallProc (Portable,II,IIci)
|
||
|
||
leaROM MtVolOK,a0 ; point to the end of the CheckRemount routine
|
||
move.l a0,jCheckRemount ; point CheckRemount vector past the routine
|
||
rts
|
||
|
||
EndProc
|
||
|
||
KillCheckRemountSickWay PatchProc jBTOpen,(Plus,SE)
|
||
|
||
cmpROM AfterBTOpenInAccessBT,(sp) ; check to see if this is AccessBT
|
||
bneOld
|
||
cmpROM After2ndAccessBTInMountVol,8(a6) ; check to see if this is the case just before remount
|
||
bneOld
|
||
|
||
addq #4,sp ; pull off return address
|
||
jsrOld ; call BTOpen, and return here
|
||
movem.l (a6)+,d3/a0 ; restore registers saved by AccessBT
|
||
addq #4,a6 ; throw away return address to MountVol
|
||
tst.w d0 ; check result of AccessBT
|
||
bneROM After2ndAccessBTInMountVol ; if we failed, go let the ROM his thing
|
||
bclr #vcbAtVOK,vcbAtrb(a2) ; from now until _Unmount the disk is inconsistent
|
||
beqROM MtCheck ; if so, go do the check (below patches will skip CheckRemount)
|
||
jmpROM MtVolOK ; otherwise, skip CheckRemount
|
||
|
||
EndProc
|
||
|
||
KillCheckRemountSickerWay PatchProc jReadBM,(SE)
|
||
|
||
cmpROM AfterReadBMInUpdateFree,(sp) ; check to see if this is UpdateFree
|
||
bneOld
|
||
cmpROM AfterUpdateFreeInMtCheck,36(a6) ; check to see if this is at the end of MtCheck
|
||
bneOld
|
||
leaROM MtVolOK,a0 ; get address for skipping CheckRemount
|
||
move.l a0,36(a6) ; return there, so we don’t branch to CheckRemount
|
||
jmpOld
|
||
|
||
EndProc
|
||
|
||
KillCheckRemountSickestWay PatchProc jGetBlock,(Plus)
|
||
|
||
cmpROM AfterGetBlockInGetBMBlk,(sp) ; check to see if this is GetBMBlk
|
||
bneOld
|
||
cmpROM AfterGetBMBlkInReadBM,16(a6) ; check to see if this is ReadBM
|
||
bneOld
|
||
cmpROM AfterReadBMInUpdateFree,20(a6) ; check to see if this is UpdateFree
|
||
bneOld
|
||
cmpROM AfterUpdateFreeInMtCheck,60(a6) ; check to see if this is at the end of MtCheck
|
||
bneOld
|
||
leaROM MtVolOK,a0 ; get address for skipping CheckRemount
|
||
move.l a0,60(a6) ; return there, so we don’t branch to CheckRemount
|
||
jmpOld
|
||
|
||
EndProc
|
||
|
||
;——————————————————————————————————————————————————————————————————————————————————————————
|
||
|
||
End
|