mac-rom/OS/HFS/FileMgrPatches.a
Elliot Nunn 4325cdcc78 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-12-26 09:52:23 +08:00

5417 lines
217 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