sys7.1-doc-wip/OS/HFS/Extensions/FileIDsSvcs.a

1098 lines
40 KiB
Plaintext

;
; Hacks to match MacOS (most recent first):
;
; <Sys7.1> 8/3/92 Reverted <SM1> by putting back the "ROM" prefixes.
; 9/2/94 SuperMario ROM source dump (header preserved below)
;
;
; File: FileIDsSvcs.a
;
; Contains: This file implements the low level File IDs facility by making a new
; type of CNode record called file threads. FThreads are similar to threads
; in record structure (key record = [ID, null]; data record = [parID, CName]
; and in functionality. (fThread data record locates the CNode record of file,
; while thread data record locates the CNode record of a directory).
; Note: File threads are keyed off the file number.
; For more details, see the documentation preceeding each call.
;
; Written by: Earsh Nandkeshwar
;
; Copyright: © 1989-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.
; • Pre-SuperMario comments follow •
; <13> 9/13/91 JSM Cleanup header.
; <12> 3/30/91 KST bb, #83912: Fixing a bug in ExchangeFile which uses a signed
; branch on an unsigned comparison (in CheckExtents).
; <11> 1/14/91 KST With BB: Documentation change.
; <10> 1/14/91 KST With BB: "FIDCreateID" should handle the situation when a file is
; MOVED or RENAMED on 6.0 system which doesn't update file thread.
; <9> 9/21/90 KST Check the file number in FidResolveID. Also, we don't delete the
; fileID if the file is not found. We just return an error.
; <8> 8/24/90 KST Delete the thread record only if the volume is writable (Re:
; <7>).
; <7> 8/23/90 KST Fixing a bug in ResolveID which didn't check for left-over
; FileID. (BRC#70809, #71502)
; <6> 8/9/90 KST Fixing a bug in FIDExchangeFiles (DeleteExts) that uses an
; invalid HINT which could cause the system to crash.
; <5> 7/30/90 dnf Rename rom references and use jsrROM macro.
;
; <4> 5/23/90 dnf Change tests for catalog record type to explicitly look for type
; "file" instead avoiding type "dir thread"
; <3> 3/16/90 dnf Clear VCBDirIDM on all routines that make BTree calls (moving
; the hint) so as to not break indexed GetCatInfo calls
; <2> 2/4/90 DNF Get rid of include of HFS70Equ.a
; <1.9> 11/15/89 EKN Tweeks from 2nd code review.
; 11/9/89 EKN Tweeks from 2nd code review.
; <1.8> 10/13/89 EKN Cleanup UpdateLastID. Added FIDGetID.
; 10/12/89 EKN Cleanup UpdateLastID some.
; <1.7> 9/29/89 EKN REWORK ExchangeFiles AND Some minor twiddles in styleish stuff
; from code review.
; 9/28/89 EKN Minor twiddles on stylish stuff from code review. MAIN Thing:
; Quit on ioErrors. Only Back out of DskFulErr. AND BIGGIE:
; Redesign ExchangeFiles and MoveExtents to make it simpler.
; BugFix: ResolveID and DeleteID should CMPI.B for cdrType tests.
; <1.6> 8/29/89 EKN FIDDeleteID and FIDResolveID doing CMPI.W instead of CMPI.B
; #cdrThdRec.
; <1.5> 7/24/89 EKN ExchangeFiles: Fix & cleanup backing out of errors A LOT.
; Cathints as parameters. Give bogusID back. Exch mod dates.
; 7/24/89 EKN Give nextID back after Exchange. Pretty up: Capitalize all
; "bsr"s; Change all JSRs to BSRs; Fix comments; Change labels.
; Cleaned up "backing out of errors" in ExchangeFiles A LOT! Add
; exchanging of mod dates (in CopyCNodeInfo). Have ExchangeFiles
; take cat hints as parameters.
; <1.4> 5/30/89 dnf Getting more kinks out
; <1.3> 5/30/89 dnf Change MOVEQ to MOVE.W for errors which changed to < -127
; <1.2> 5/30/89 dnf Changes to support HFS7.0 Enhancements ptch
; 5/29/89 EKN Created file.
; <1.1> 5/24/89 rwh include inc.sum.a to fix build problems
; <1.0> 5/15/89 EKN New for FileIDs facility.
;
; To Do:
;
;-- THINGS TO DO (FUTURE ENHANCEMENTS):
;-- 1) Make common setup routine for 4 external calls, if we decide not to vector all 4.
;
;_________________________________________________________________________________
;
; External
; Routines: FIDCreateID - Creates a file thread to an HFS existing file.
; FIDDeleteID - Destroys the file thread to the HFS File.
; FIDGetID - Returns the value of the file ID if it exists.
; FIDResolveID - Given a fileID, returns the parID and cname of the file.
; FIDExchangeFiles - Swaps the data of the files. For doing "save as".
;
; Note about VCBDirIDM: dnf <2>
; The indexed GetCatInfo call (and CMGetOff below it) depend on the fact
; that if VCBDirIDM is non-zero then the current btree hint is at VCBOffsM
; in directory VCBDirIDM. Thus, on an indexed call CMGetOff can do a
; quick BTGetRec instead of a search.
;
; All of the catalog manager routines which can change the hint clear
; VCBDirIDM. However, the FileIDsSvcs routines in this file don't use
; any of the CMxxx routines, so VCBDirIDM needs to be cleared manually in
; here.
;
;_________________________________________________________________________________
BLANKS ON ; need semicolons to separate comments now
STRING ASIS ; strings are the way they look
PRINT PUSH
PRINT OFF ; don't send lines to assembly listing file
LOAD 'StandardEqu.d'
include 'LinkedPatchMacros.a'
PRINT POP ; okay, send the lines for the listing
PRINT NOGEN ; don't show the macro expansions
FileIDsSvcs PROC EXPORT
EXPORT FIDCreateID, FIDDeleteID, FIDGetID, FIDResolveID, FIDExchangeFiles
;_________________________________________________________________________________
;
; Routine: FIDCreateID (Create a file thread)
;
; Function: FIDCreateID creates a file thread to an HFS file. The file thread
; is similar to a directory thread, with it's key being a
; [fileCNodeID, NULL], instead of a [dirID, NULL]. If the file thread
; exists, fidExists is returned in D0, and the ID in D1 anyway.
;
; Input: A2.L - VCB pointer
; D0.L - DirID
; A0.L - CName pointer
;
; Output: D0.W - result code
; 0 = ok
; CMnotfound = CNode not found
; CMExists = CNode for file thread exists
; cmFThdDirErr = file is a directory, not a file
; -n = IO Error
; D1.L - file thread
; Modification History:
; <11Jan91> KSCT File with a thread could be moved on the 6.0 system without updating the
; thread record. FIDCreateID should check and do the update if necessary.
;_______________________________________________________________________________________________
FIDCrRegs reg D2-D6/A1/A3-A4
FIDCreateID:
;
; do some initial stuff
;
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
MOVEM.L FIDCrRegs, -(A6) ; save registers
MOVE.L D0,D6 ; save the par ID
MOVEA.L A0,A3 ; save the cname ptr
jsrROM ROMCMSETUP ; set up key storage pointed to by A4 ex<SM1><Sys7.1> Put back "ROM"
MOVEQ #0,D5 ; clear fileID, in case we return early due to errors
CLR.L VCBDirIDM(A2) ; invalidate current DirID marker
;
; locate the file record
;
MOVEQ #0,D2 ; no catalog hint
jsrROM ROMLOCCREC ; ex<SM1><Sys7.1> Put back "ROM"
BNE @CrExit ; it's not there, just exit with what's in D0
CMPI.B #cdrFilRec, cdrType(A1); better be a file (type=2)
BNE.S @CrNotAFileErr
MOVE.L D2,D4 ; save cat hint for later
MOVE.B FilFlags(A1),D3 ; save the flag byte <11Jan91 #10>
;
; build the key for the file thread
;
MOVE.L filFlNum(A1), D0 ; get file's CNode id for fthread key
MOVE.L D0,D5 ; keep fileID for later
SUBA.L A0,A0 ; use NULL for fthread key name
LEA ckrOff(A4), A1 ; use this storage for the fthread key
jsrROM ROMBUILDKEY ; fill in the key ex<SM1><Sys7.1> Put back "ROM"
;
; and do the data of the file thread
;
LEA cdrOff(A4),A1 ; clear
MOVE.W #(lenthd/2)-1,D0 ;
@1 CLR.W (A1)+ ;
DBRA D0,@1 ; ...the record first
LEA cdrOff(A4),A1
MOVE.B #cdrfThdRec, cdrType(A1)
MOVE.L D6, thdParID(A1) ; the saved parID
MOVEA.L A3, A0 ; the cname ptr
LEA thdCName(A1), A1
jsrROM ROMUPDCNAME ; ex<SM1><Sys7.1> Put back "ROM"
;
; Insert the file thread in the catalog btree
;
MOVE.W VCBCtRef(A2), D0 ; refnum of catalog file
MOVE.W #lenthd, D1
LEA cdrOff(A4), A1
LEA ckrOff(A4), A0
jsrROM ROMBTINSERT ; ex<SM1><Sys7.1> Put back "ROM"
BNE.S @CrBTErr ; could be disk full, so stop
;
; finally, turn on thread flag in file CNode. Too bad, gotta find it again
;
MOVE.L D4, D2 ; use saved cat hint
MOVE.L D6, D0 ; dirID or parent dirID
MOVEA.L A3, A0 ; CName pointer
jsrROM ROMLOCCREC ; we know it's there ex<SM1><Sys7.1> Put back "ROM"
BSET #fThreadFlag,FilFlags(A1); set the flag
MOVE.W VCBCtRef(A2),D0 ; refnum of catalog file
jsrROM ROMBTUPDATE ; ex<SM1><Sys7.1> Put back "ROM"
BNE.S @CRExit ; if there was an error, catalog could be shot!
;
; get it out to disk
;
jsrROm ROMCMFLUSH ; flush the catalog ex<SM1><Sys7.1> Put back "ROM"
BRA.S @CrExit
;
; handle errors
;
@CrNotAFileErr
MOVE.W #cmFThdDirErr,D0 ; Error "cmFThdDirErr = it is a directory"
BRA.S @CrExit
@CrBTErr CMP.W #BTExists,D0
BNE.S @CrExit ; It maybe out of space. Let it pass through.
;; Thread already exists, check if the thread info is consistent. <11Jan91 #10>
;; Fixing a bug if the file has been MOVED or RENAMED on a 6.0 system which doesn't update FID info!
MOVE.L D5,D0 ; file number <11Jan91 #10>
SUBA.L A0,A0 ; no Cname <11Jan91 #10>
MOVEQ #0,D2 ; no hint <11Jan91 #10>
jsrROM ROMLOCCREC ; locate thread record <11Jan91 #10> ex<SM1><Sys7.1> Put back "ROM"
BNE.S @7 ; bad time for error <11Jan91 #10>
CMP.B #cdrfThdRec,cdrType(A1) ; file thread type? <11Jan91 #10>
BNE.S @7 ; serious problem <11Jan91 #10>
BTST #fThreadFlag,D3 ; flag should be set <11Jan91 #10>
BEQ.S @7 ; serious problem <11Jan91 #10>
CMP.L thdParID(A1),D6 ; has the file been moved? <14Jan91 #11>
BEQ.S @4 ; NO, (this is the common case) <11Jan91 #10>
MOVE.L D6,thdParID(A1) ; otherwise, update it <11Jan91 #10>
MOVEQ #0,D3 ; D3 = 0 means we changed the file <11Jan91 #10>
@4
LEA thdCName(A1),A1 ; the dest cname ptr <11Jan91 #10>
MOVEA.L A3,A0 ; the source cname ptr <11Jan91 #10>
MOVEM.L A0/A1,-(SP) ; save two names <11Jan91 #10>
MOVEQ #0,D0 ; clear D0 <11Jan91 #10>
MOVE.B (A0)+,D0 ; D0(high order word) = length of A0 <11Jan91 #10>
SWAP D0 ; swap it <11Jan91 #10>
MOVE.B (A1)+,D0 ; D0( low order word) = length of A1 <11Jan91 #10>
_RelString ; has the file been renamed? <14Jan91 #11>
MOVEM.L (SP)+,A0/A1 ; restore the names <11Jan91 #10>
BEQ.S @5 ; NO, (this is the common case) <11Jan91 #10>
; A0/A1 preserved across the call <11Jan91 #10>
MOVEQ #1,D0 ; include length byte <11Jan91 #10>
ADD.B (A0),D0 ; D0.L = length of source <11Jan91 #10>
_BlockMove ; A0 -> A1 <11Jan91 #10>
BRA.S @6 ; always updateBT <11Jan91 #10>
@5 TST.W D3 ; have we changed the file? <11Jan91 #10>
BNE.S @7 ; no <11Jan91 #10>
@6 MOVE.W VCBCtRef(A2),D0 ; refnum of catalog file (input = D0/D2) <11Jan91 #10>
jsrROM ROMBTUPDATE ; mark the node (D2) dirty <11Jan91 #10> ex<SM1><Sys7.1> Put back "ROM"
BNE.S @CrExit ; bad time for error <11Jan91 #10>
jsrROm ROMCMFLUSH ; flush the catalog (input = A2) <11Jan91 #10> ex<SM1><Sys7.1> Put back "ROM"
BNE.S @CrExit ; bad time for error <11Jan91 #10>
@7 MOVE.W #CMExists,D0 ; Translate error "CMExists = cnode already there"
;
; cleanup and return
;
@CrExit MOVE.L D5,D1 ; return the file id
ADD #lenCMVars,A6 ; de-allocate memory for CM vars
MOVEM.L (A6)+, FIDCrRegs ; restore regs
MOVE.L (A6)+,-(SP) ; put return address back on stack
TST.W D0 ; set condition codes
RTS ; exit FIDCreateID
; sameNameP
; Input: A1 = cat. file name
; A3 = user's file name
; Output: BEQ if 2 names are the same
LEA thdCName(A1), A1
jsrROM ROMUPDCNAME ; ex<SM1><Sys7.1> Put back "ROM"
;_________________________________________________________________________________
;
; Routine: FIDDeleteID (Delete a file thread)
;
; Function: FIDDeleteID invalidates a file id, by removing the thread from the
; from the cat file and by turning off the file CNode link flag.
;
; Input: A2.L - VCB pointer
; D0.L - file thread
;
; Output: D0.W - result code
; 0 = ok
; CMnotfound = CNode not found
; cmFThdGone = File thread not found
; cmFThdDirErr = file is a directory, not a file
; -n = IO error
;_________________________________________________________________________________
FIDDelRegs reg D1-D7/A1/A3-A4
FIDDeleteID:
;
; do some initial stuff
;
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
MOVEM.L FIDDelRegs,-(A6) ; save registers
jsrROM ROMCMSETUP ; set up key storage pointed to by A4 ex<SM1><Sys7.1> Put back "ROM"
MOVE.L D0, D5 ; save fileID
MOVEQ #0, D7 ; save error in here, if CNode for file is missing
CLR.L VCBDirIDM(A2) ; invalidate current DirID marker
;
; locate the file thread
;
MOVEQ #0, D2 ; no catalog hint
SUBA.L A0, A0 ; use NULL for fthread key.
jsrROM ROMLOCCREC ; ex<SM1><Sys7.1> Put back "ROM"
BNE.S @DlNoThdErr ; it's not there
CMPI.B #cdrFThdRec, cdrType(A1); better be a file
BNE.S @DlNotAFileErr
;
; and then find the file
;
MOVEQ #0, D2 ; no catalog hint
MOVE.L thdParID(A1), D0 ; get file's dir id from fthread data
LEA thdCName(A1), A0 ; and it's name
jsrROM ROMLOCCREC ; ex<SM1><Sys7.1> Put back "ROM"
BEQ.S @DlUnsetFlg
MOVE.W D0, D7 ; need to return the error later
BNE.S @DlDelThd ; whoops, it's not there...well, delete the thread
;
; ** NOTE: If LocCNode were modified for fthreads, the two LocCRecs above could be avoided
;
; turn off thread flag in file CNode
;
@DlUnsetFlg BCLR #fThreadFlag,FilFlags(A1)
MOVE.W VCBCtRef(A2), D0 ; refnum of catalog file
jsrROM ROMBTUPDATE ; tell the btree ex<SM1><Sys7.1> Put back "ROM"
BNE.S @DlExit ; if there's an error, the catalog could be shot!
;
; Delete the file thread in the catalog btree
;
@DlDelThd MOVE.L D5, D0 ; file id
SUBA.L A0,A0 ; use NULL for fthread key
LEA ckrOff(A4), A1 ; use this storage for fthread key
jsrROM ROMBUILDKEY ; fill in the key ex<SM1><Sys7.1> Put back "ROM"
LEA ckrOff(A4), A0 ; get the storage again
MOVE.W VCBCtRef(A2), D0 ; refnum of catalog file
jsrROM ROMBTDELETE ; delete file thread ex<SM1><Sys7.1> Put back "ROM"
BNE.S @DlExit ; we know it exists, so must be IOError...
; ...Oh well, leave the cnode's fthread flag off.
;
; get it out to disk.
;
jsrROM ROMCMFLUSH ; flush the catalog ex<SM1><Sys7.1> Put back "ROM"
MOVE.W D7, D0 ; should be clear, unless file CNode was missing
BRA.S @DlExit
;
; handle errors
;
@DlNoThdErr CMP.W #CMNotFound, D0 ; translate the error so callee knows it wasn't a fnfErr
BNE.S @DlExit
MOVE.W #cmFThdGone, D0
BRA.S @DlExit
@DlNotAFileErr
MOVE.W #cmFThdDirErr, D0 ; Error "cmFThdDirErr = it's a directory"
;
; cleanup and return
;
@DlExit ADD #lenCMVars,A6 ; de-allocate memory for CM vars
MOVEM.L (A6)+, FIDDelRegs ; restore regs
MOVE.L (A6)+,-(SP) ; put return address back on stack
TST.W D0 ; set condition codes
RTS ; exit FIDDeleteID
;_________________________________________________________________________________
;
; Routine: FIDGetID (Get a file thread)
;
; Function: FIDGetID returns a file thread to an HFS file if it exists.
;
; Input: A2.L - VCB pointer
; D0.L - DirID
; A0.L - CName pointer
;
; Output: D0.W - result code
; 0 = ok
; CMnotfound = CNode not found
; cmFThdGone = File thread not found
; cmFThdDirErr = file is a directory, not a file
; -n = IO Error
; D1.L - file thread
;_________________________________________________________________________________
FIDGtRegs reg A1/A4/D2
FIDGetID:
;
; do some initial stuff
;
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
MOVEM.L FIDGtRegs, -(A6) ; save registers
jsrROM ROMCMSETUP ; set up key storage pointed to by A4 ex<SM1><Sys7.1> Put back "ROM"
MOVEQ #0, D5 ; clear fileID, in case we return early due to errors
CLR.L VCBDirIDM(A2) ; invalidate current DirID marker
;
; locate the file record
;
MOVEQ #0, D2 ; no catalog hint
jsrROM ROMLOCCREC ; ex<SM1><Sys7.1> Put back "ROM"
BNE @GtExit ; it's not there, just exit with what's in D0
CMPI.B #cdrFilRec, cdrType(A1); better be a file
BNE.S @GtNotAFileErr
;
; finally, if the file thread flag is set, grab the fileID and set D0
;
BTST #fThreadFlag,FilFlags(A1); tst the flag
BEQ.S @GtNoThdErr
MOVE.L filFlNum(A1),D1 ; save file number
BRA.S @GtExit1
;
; handle errors
;
@GtNotAFileErr
MOVE.W #cmFThdDirErr, D0 ; Error "cmFThdDirErr = it is a directory"
BRA.S @GtExit
@GtNoThdErr MOVE.W #cmFThdGone, D0 ; Error "cmFThdGone = file ID doesn't exist"
;
; cleanup and return
;
@GtExit MOVE.L D5, D1 ; return the file id
@GtExit1 ADD #lenCMVars,A6 ; de-allocate memory for CM vars
MOVEM.L (A6)+, FIDGtRegs ; restore regs
MOVE.L (A6)+,-(SP) ; put return address back on stack
TST.W D0 ; set condition codes
RTS ; exit FIDGetID
;_________________________________________________________________________________
;
; Routine: FIDResolveID (Resolves a file thread)
;
; Function: FIDResolveID finds the dirID and CName for a file, given it's thread.
;
; Input: A2.L - VCB pointer
; D0.L - file thread
; A0.L - CName pointer (empty storage)
;
; Output: D0.W - result code
; 0 = ok
; cmFThdDirErr = file is a directory, not a file
; CMNotFound = Cnode not found (where cnode is file thread)
; -n = IO Error
; A0.L - CName pointer (filled in storage)
; D1.L - dirID
; 23Aug90 KST Checking for possible dangling fileID and delete the thread record if it is.
;_________________________________________________________________________________
FIDResRegs reg D2-D6/A1/A3-A5 ; <why saving all those regs?? 23Aug90>
FIDResolveID:
;
; do some initial stuff
;
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
MOVEM.L FIDResRegs,-(A6) ; save registers
MOVE.L D0,D3 ; save fileID
jsrROM ROMCMSETUP ; set up key record and data storage ex<SM1><Sys7.1> Put back "ROM"
MOVEA.L A0, A3 ; save cName ptr
CLR.L VCBDirIDM(A2) ; invalidate current DirID marker
;
; locate the file thread
;
;; MOVEQ #0, D2 ; no catalog hint <23Aug90>
SUBA.L A0, A0 ; use NULL for file name.
jsrROM ROMLOCCREC ; ex<SM1><Sys7.1> Put back "ROM"
BNE.S @RsExit ; it's not there, pass error on up
CMPI.B #cdrFThdRec, cdrType(A1); better be a file
BNE.S @RsNotAFileErr
;
; extract the info
MOVE.L thdParID(A1), D0 ; get file's dir id from fthread data
LEA thdCName(A1), A0 ; and it's name
MOVEM.L D0/D2/A0,-(A6) ; save dirID/hint/name <23Aug90>
MOVEQ #0, D2 ; no catalog hint <23Aug90>
jsrROM ROMLOCCREC ; does the file exist? <23Aug90> ex<SM1><Sys7.1> Put back "ROM"
MOVEM.L (A6)+,D1/D2/A0 ; restore dirID/hint/name <23Aug90>
BNE.S @dangling ; it's not there <23Aug90>
;; we found the file, but is this the file we really want? <21Sep90>
CMP.L filFlNum(A1),D3 ; file number match? <21Sep90>
BNE.S @dangling ; it's not <21Sep90>
MOVEA.L A3, A1 ; the cname ptr
jsrROM ROMUPDCNAME ; ex<SM1><Sys7.1> Put back "ROM"
MOVEQ #0, D0 ; to be safe
;
; cleanup and exit
;
@RsExit ADD #lenCMVars,A6 ; de-allocate memory for CM vars
MOVEM.L (A6)+, FIDResRegs ; restore regs
MOVE.L (A6)+,-(SP) ; put return address back on stack
TST.W D0 ; set condition codes
RTS ; exit FIDResolveID
;
; handle errors
;
@RsNotAFileErr
MOVE.W #cmFThdDirErr, D0 ; Error "cmFThdDirErr = it's a directory"
BRA.S @RsExit
@dangling
;; This is a dangling fID, if the volume is writable, <24Aug90>
;; then delete the thread record in the catalog btree. <23Aug90>
; jsrROM CVFLGS ; is VOL writable? (IN: A2=vcb) <24Aug90>
; BNE.S @2 ; no, don't bother with delete <24Aug90>
; MOVE.L D3,D0 ; file id <23Aug90>
; SUBA.L A0,A0 ; use NULL for fthread key <23Aug90>
; LEA ckrOff(A4), A1 ; use this storage for fthread key <23Aug90>
; jsrROM BUILDKEY ; fill in the key <23Aug90>
; LEA ckrOff(A4),A0 ; get the storage again <23Aug90>
; MOVE.W VCBCtRef(A2),D0 ; refnum of catalog file <23Aug90>
; jsrROM BTDELETE ; delete file thread <23Aug90>
; BNE.S @2 ; this must be IOError or VolLocked <23Aug90>
; jsrROM CMFLUSH ; flush the catalog <23Aug90>
;; Now we just return an error, we don't delete the Fid. <21Sep90>
@2 MOVE.W #badFidErr,D0 ; treat it as fidNotFound error <21Sep90>
BRA.S @RsExit ; and return <23Aug90>
;_________________________________________________________________________________
;
; Routine: FIDExchangeFiles
;
; Function: FIDExchangeFiles exchanges the data of the source and dest files. It has to
; locate the CNodes, check for extents in extents file, exchange them,
; and then exchange the CNode file record info pertaining to data
; (lengths, extents, and mod dates).
; All other CNode info remains unchanged (finder info, create/backup dates).
; FIDExchangeFiles gives apps the functionality of a "safe save".
;
; Input: A2.L - VCB pointer
; D1.L - destination directory id
; D0.L - source directory id
; A1.L - destination name
; A0.L - source name
; D2.L - src cat hint to CNode rec
; D3.L - dest cat hint to Cnode rec
;
; Output: D0.W - result code
; 0 = ok
; cmFThdDirErr = file is a directory, not a file
; CMNotFound = Cnode for source or dest not found
; -n = IO error
;_________________________________________________________________________________
FIDExchRegs reg D1-D7/A0-A1/A3-A5
FIDExchangeFiles:
;
; STEP 0: DO SOME INITIAL STUFF
;
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
MOVEM.L FIDExchRegs, -(A6) ; save registers
SUB #lenFIDSwapVars, A6 ; grab some storage
MOVEA.L A6, A4 ; set storage to A4
MOVEA.L A1, A3 ; save dest Cname for later
MOVEA.L A0, A5 ; save src CName for later
MOVE.L D1, destDIDOff(A4)
MOVE.L D0, srcDIDOff(A4)
MOVE.L D3, D7 ; save dest catHint for later
MOVE.L D2, D6 ; save src catHint for later
CLR.L VCBDirIDM(A2) ; invalidate current DirID marker
;
; STEP 1: GRAB THE CNODE RECORDS AND CHECK FOR EXTENTS IN EXTENT FILE
;
; locate the source file, test for extents in extent file, and copy the cat record for later
;
CLR.W extFlgsOff(A4) ; assume no extents in extents file
jsrROM ROMLOCCREC ; A0, D0, D2 already set up from input ex<SM1><Sys7.1> Put back "ROM"
BNE @ExExit ; either CNode not there or io error
CMPI.B #cdrFilRec, cdrType(A1); better be a file
BNE @ExNotAFileErr
MOVE.L D2, D6 ; save the catHint in case it changed
LEA filExtRec(A1), A0 ; are there extents in source data fork
MOVE.L filPyLen(A1), D0
BSR CheckExtents
TST.W D0
BEQ.S @ExNxt
BSET #srcExt, extFlgsOff(A4)
BRA.S @ExNxt1 ; no need to check resource fork now
@ExNxt LEA filRExtRec(A1), A0 ; are there extents in source resource fork
MOVE.L filRPyLen(A1), D0
BSR CheckExtents
TST.W D0
BEQ.S @ExNxt1
BSET #srcExt, extFlgsOff(A4)
@ExNxt1 MOVEA.L A1, A0 ; source - the cnode in memory
LEA srcCNode(A4), A1 ; dest - the cnode on the stack
MOVEQ #lencdr, D0 ; length of file record
_BlockMove
;
; locate the dest file, test for extents in extent file, and copy the cat record for later
;
MOVE.L D7, D2 ; catalog hint
MOVE.L destDIDOff(A4), D0 ; it's dir id
MOVEA.L A3, A0 ; it's name
jsrROM ROMLOCCREC ; keep the cat hint in D2 for the BTUpdate later ex<SM1><Sys7.1> Put back "ROM"
BNE @ExExit ; either CNode not there or io error (callee knows it's there!)
CMPI.B #cdrFilRec, cdrType(A1); better be a file
BNE @ExNotAFileErr
MOVE.L D2, D7 ; save the catHint in case it changed
LEA filExtRec(A1), A0 ; are there extents in dests data fork
MOVE.L filPyLen(A1), D0
BSR CheckExtents
TST.W D0
BEQ.S @ExNxt2
BSET #destExt, extFlgsOff(A4)
BRA.S @ExNxt3 ; no need to check resource fork now
@ExNxt2 LEA filRExtRec(A1), A0 ; are there extents in dests resource fork
MOVE.L filRPyLen(A1), D0
BSR CheckExtents
TST.W D0
BEQ.S @ExNxt3
BSET #destExt, extFlgsOff(A4)
@ExNxt3 MOVEA.L A1, A0 ; source - the cnode in memory
LEA destCNode(A4), A1 ; dest - the cnode on the stack
MOVEQ #lencdr, D0 ; length of file record
_BlockMove
;
; STEP 2: EXCHANGE THE EXTENTS KEY IN THE EXTENT FILE (IF ANY)
; NOTE: We could avoid some backing out of errors on DskFullErr if we checked free space first, right?
;
MOVEQ #fidBogusExtID, D0 ; first delete any existing extents of this value (resulting from a crash)
BSR DeleteExts
BNE @ExExit ; best to stop
LEA srcCNode(A4), A0 ; get the source cnode record
LEA destCNode(A4), A1 ; get the dest cnode record
BTST #srcExt, extFlgsOff(A4) ; does the source file have extents?
BNE.S @ExYesSrc ; yep, =>
BTST #destExt, extFlgsOff(A4) ; nope, so test dest
BNE.S @ExNoSrcExt ; yep, no source extents, but some dest extents
BRA.S @ExGetOut ; YIPPIE!!! No extents in extents files!
@ExYesSrc BTST #destExt, extFlgsOff(A4) ; src has extents, what about dest?
BEQ.S @ExNoDestExt ; no dest extents, so =>
;
; GROSS! Extents in both files. Now we got grab a new ID to move one set of extents into
;
MOVEQ #fidBogusExtID, D5 ; get the ID reserved for exchanging extents
MOVE.L filFlNum(A0), D1 ; move this ID to ...
MOVE.L D5, D2 ; ...this one
BSR MoveExtents ; change source extents to bogus id extents
BNE @ExUndo1
MOVE.L filFlNum(A1), D1 ; move this ID to ...
MOVE.L filFlNum(A0), D2 ; ... this one
BSR MoveExtents ; change dest extents to source extents
BNE @ExUndo2
MOVE.L D5, D1 ; move this ID to ...
MOVE.L filFlNum(A1), D2 ; ... this one
BSR MoveExtents ; change bogus id extents to dest extents
BNE.S @ExUndo3
BRA.S @ExGetOut
@ExNoSrcExt MOVE.L filFlNum(A1), D1 ; move this ID to...
MOVE.L filFlNum(A0), D2 ; ... this one
BSR MoveExtents
BNE @ExUndo0
BRA.S @ExGetOut
@ExNoDestExt MOVE.L filFlNum(A0), D1 ; move this ID to ...
MOVE.L filFlNum(A1), D2 ; ... this one
BSR MoveExtents
BNE @ExUndo0
;
; STEP 3: CHANGE THE DATA IN THE CNODES
;
; find the source cnode and put dest info in it
;
@ExGetOut MOVE.L D6, D2 ; catalog hint
MOVEA.L A5,A0 ; it's name
MOVE.L srcDIDOff(A4), D0 ; it's dir id
jsrROM ROMLOCCREC ; ex<SM1><Sys7.1> Put back "ROM"
BNE @ExBusted
LEA destCNode(A4), A0 ; from saved dest (A0) to memory src (A1)
BSR CopyCNodeInfo
MOVE.W VCBCtRef(A2), D0
jsrROM ROMBTUPDATE ; tell the btree ex<SM1><Sys7.1> Put back "ROM"
BNE @ExExit ; if there's an error, the catalog could be shot!
;
; find the destination cnode and put source info in it
;
MOVE.L D7, D2 ; catalog hint
MOVEA.L A3, A0 ; it's name
MOVE.L destDIDOff(A4), D0 ; it's dir id
jsrROM ROMLOCCREC ; keep cat hint in D2 for the BTUpdate later ex<SM1><Sys7.1> Put back "ROM"
BNE @ExBusted
LEA srcCNode(A4), A0 ; from saved src (A0) to memory dest (A1)
BSR CopyCNodeInfo
MOVE.W VCBCtRef(A2), D0
jsrROM ROMBTUPDATE ; tell the btree ex<SM1><Sys7.1> Put back "ROM"
BNE @ExExit ; if there's an error, the catalog could be shot!
BRA.S @ExFinishUp
;
; STEP 4: ERROR HANDLING SECTION (just deal with DiskFulErr. With the others, stop and don't flush!)
;
@ExUndo3 CMP.W #dskFulErr, D0
BNE @ExExit ; don't flush a thing on other errors!
MOVE.L filFlNum(A1), D0 ; delete dest extents
BSR DeleteExts
BNE.S @ExExit ; we are doomed. Just QUIT!
MOVE.L filFlNum(A1), D2
MOVE.L filFlNum(A0), D1
BSR MoveExtents ; move source extents back into dest extents
BNE.S @ExExit ; we are doomed. Just QUIT!
BRA.S @ExUndo2a
@ExUndo2 CMP.W #dskFulErr, D0
BNE.S @ExExit ; don't flush a thing on other errors!
@ExUndo2a MOVE.L filFlNum(A0), D0 ; delete src extents
BSR DeleteExts
BNE.S @ExExit ; we are doomed. Just QUIT!
MOVE.L filFlNum(A0), D2
MOVE.L D5, D1
BSR MoveExtents ; move nextID extents back into source extents
BNE.S @ExExit ; we are doomed. Just QUIT!
BRA.S @ExUndo1a
@ExUndo1 CMP.W #dskFulErr, D0
BNE.S @ExExit ; don't flush a thing on other errors!
@ExUndo1a MOVE.L D5, D0 ; delete nextID extents
BSR DeleteExts
BNE.S @ExExit ; we are doomed. Just QUIT!
BRA.S @ExFinishErr
@ExUndo0 CMP.W #dskFulErr, D0
BNE.S @ExExit ; don't flush a thing on other errors!
MOVE.L D2, D0
BSR DeleteExts ; delete src or dest
BNE.S @ExExit ; we are doomed. Just QUIT!
;
; STEP 5: ALL DONE....ALMOST...
;
@ExFinishErr
jsrROM ROMCMFLUSH ; flush the catalog ex<SM1><Sys7.1> Put back "ROM"
jsrROM ROMXFFLUSH ; flush the extent file (unneeded for common case, but it's cheap)
MOVE.W #dskFulErr, D0 ; well, report the original error even if the flushes croaked.
BRA.S @ExExit
@ExNotAFileErr
MOVE.W #cmFThdDirErr, D0 ; Error "cmFThdDirErr = it's a directory"
BRA.S @ExExit
@ExBusted MOVE.W #cmBadNews, D0 ; the cnode record got lost after we looked it up
BRA.S @ExExit
;
; cleanup and return
;
@ExFinishUp
MOVEQ #0, D0 ; no errors....yet...
jsrROM ROMCMFLUSH ; flush the catalog ex<SM1><Sys7.1> Put back "ROM"
jsrROM ROMXFFLUSH ; flush the extent file (unneeded for common case, but it's cheap)
@ExExit ADD #lenFIDSwapVars,A6 ; de-allocate memory for CM vars
MOVEM.L (A6)+, FIDExchRegs ; restore regs
MOVE.L (A6)+,-(SP) ; put return address back on stack
TST.W D0 ; set condition codes
RTS ; exit FIDExchangeFiles
;__________________________________________________________________________________
; INTERNAL ROUTINES
;__________________________________________________________________________________
;__________________________________________________________________________________
; Routine: CheckExtents
;
; Function: Checks the extents in CNode to see if there are any more in the
; extent file.
;
; Input: A0.L - addr(extent in CNODE of data or resource fork)
; D0.L - peof of file
; A2.L - vcb pointer
;
; Output: D0.W - first FABN in extent file (or zero if none in extent file)
; A0.L - unchanged from input
;
; <30Mar91> KSCT Fixing a bug in ExchangeFile which uses a signed
; branch on an unsigned comparison.
;__________________________________________________________________________________
CheckExtents:
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
MOVEM.L D1-D4, -(A6) ; save registers
MOVE.L D0, D3 ; extract peof (it's in bytes and a long word)
TST.L D3 ; convert it to blocks
BEQ.S @ChNoneThere ; zero length file
SUBQ.L #1, D3
DIVU.W vcbAlBlkSiz+2(A2), D3 ; the high word could have trash in it, but we don't care
ADDQ.W #1, D3 ; now the peof is in blocks and only a word size
@1 CLR.W D1 ; extent offset in extent record
MOVEQ #0, D0 ; sum of extents allocation blocks
MOVEQ #numExts-1, D2 ; loop through extents checking for last FABN
@2 MOVE.W xdrNumABlks(A0, D1.W), D4 ; make sure we only pick up a word!
ADD.W D4, D0
CMP.W D3, D0 ; check for eof
BHS.S @ChNoneThere ; greater than or equal (extents can add past eof if 'Close" crashes w/o truncating new clump)
; changed from BGE to BHS <30Mar91 #12>
ADDQ.W #lenExt, D1 ; bump to next extent
DBRA D2, @2
BRA.S @ChExit
@ChNoneThere MOVEQ #0, D0
@ChExit MOVEM.L (A6)+, D1-D4 ; restore regs
MOVE.L (A6)+,-(SP) ; put return address back on stack
RTS
;_________________________________________________________________________________
; CopyCNodeInfo
;
; Routine: CopyCNodeInfo
;
; Function: Copies the info in the file cnode record relating to data exchanges.
;
; NOTE: If the layout in the catalog file record changes between bytes 24-98,
; this WILL BREAK!
;
; Input: A0.L - from
; A1.L - to
;_________________________________________________________________________________
CopyCNodeInfo:
MOVE.L (SP)+, -(A6)
MOVEM.L D0/A0-A1, -(A6)
ADDA.W #filStBlk, A1 ; set up to copy filStBlk, filLgLen, filPyLen, ...
ADDA.W #filStBlk, A0 ; ...filRStBlk, filRLgLen, filRPyLen
MOVE.W #((filCrDat-filStBlk)/2)-1, D0
@1 MOVE.W (A0)+, (A1)+
DBRA D0, @1
ADDA.W #4, A0 ; skip past the filCrDat
ADDA.W #4, A1 ; skip past the filCrDat
MOVE.L (A0)+, (A1)+ ; copy the filMdDat
ADDA.W #(filExtRec-filBkDat), A1 ; set up to copy filExtRec and filRExtRec
ADDA.W #(filExtRec-filBkDat), A0
MOVE.W #((filResrv-filExtRec)/2)-1, D0
@2 MOVE.W (A0)+, (A1)+
DBRA D0, @2
MOVEM.L (A6)+, D0/A0-A1
MOVE.L (A6)+, -(SP)
RTS
;__________________________________________________________________________________
; Routine: CopyExtentInfo
;
; Function: Copy the key and data extents to a storage.
;
; Input: A0.L - addr(key)
; A1.L - addr(data)
; A3.L - addr(extent storage)
;
; Output: A3.L - addr(end of extent storage, all filled in)
;__________________________________________________________________________________
CopyExtentInfo:
MOVE.L (SP)+, -(A6) ; save return address on A6 stack
MOVEM.L D0/A0-A1, -(A6)
MOVEQ #(lenxkr/2)-1, D0 ; copy the key
@1 MOVE.W (A0)+, (A3)+
DBRA D0, @1
MOVEQ #(lenxdr/2)-1, D0 ; copy the data
@2 MOVE.W (A1)+, (A3)+ ; A3 should be right place at start
DBRA D0, @2
MOVEM.L (A6)+, D0/A0-A1
MOVE.L (A6)+, -(SP) ; put return address back on stack
RTS
;__________________________________________________________________________________
; Routine: DeleteExts
;
; Function: Delete all extents in extent file that have the ID given.
;
; Input: A4.L - fidSwapVars pointer (pointer to variable storage)
; A2.L - vcb pointer
; D0.L - ID of file with extents to delete
;
; Output: D0.W - errors
;__________________________________________________________________________________
DXRegs reg D1-D3/D6/A0-A1
DeleteExts:
;
; set up some initial stuff
;
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
MOVEM.L DXRegs, -(A6) ; save registers
MOVE.L D0, D6 ; save it for later
;
; set up key to delete
;
LEA tempKeyOff(A4), A0 ; get the blank key we started out with
MOVE.B #lenxkr-1, xkrKeyLen(A0)
CLR.B xkrFkType(A0) ; clear the fork
MOVE.L D0, xkrFNum(A0) ; set the key's ID
CLR.W xkrFABN(A0) ; clear the FABN
;
; start grabbing extents from extent file and delete them
;
@DXNxtDelete
MOVEQ #0, D2 ; no hint (after BTDelete) <09Aug90>
MOVE.W VCBXTRef(A2), D0 ; extents file refnum
jsrROM ROMBTSEARCH ; search BTree for extent record ex<SM1><Sys7.1> Put back "ROM"
CMP.W #BTNotFound, D0
BNE.S @DXExit ; has to be an ioerror!
MOVEQ #0, D1 ; get what it's pointing at
MOVE.W VCBXTRef(A2), D0 ; extents file refnum
jsrROM ROMBTGETRECORD ; this is the first record we want for this file ex<SM1><Sys7.1> Put back "ROM"
BNE.S @DXBtErr
CMP.L xkrFNum(A0), D6
BNE.S @DXExit ; Yippie!!! We're done!
MOVE.W VCBXTRef(A2), D0 ; extent file's refnum
jsrROM ROMBTDELETE ; and delete from btree (AO has key) ex<SM1><Sys7.1> Put back "ROM"
BNE.S @DXExit
LEA tempKeyOff(A4), A0 ; look again
BRA.S @DXNxtDelete
;
; handle errors
;
@DXBTErr CMP.W #BTNotFound, D0
BNE.S @DXExit ; let the other errors go on up
CLR.W D0
;
; cleanup and return
;
@DXExit MOVEM.L (A6)+, DXRegs ; restore regs
MOVE.L (A6)+,-(SP) ; put return address back on stack
TST.W D0 ; set condition codes
RTS
;_________________________________________________________________________________
; MoveExtents
;
; Routine: MoveExtents
;
; Function: Reads in up to four extent records at a time, change the ID in the keys,
; and inserts them back in the extent btree.
; Then searches again one-by-one for the original extents and deletes them.
;
; NOTE: Picking four extent records up is just a guess. Perhaps more is
; necessary. But since we're getting the storage for them off the A6
; stack, best not take too much. However, the fewer we pick up the more
; disk arm movement will occur during the "search, edit, insert"
; per extent record cycle
;
; Input: D1.L - source key ID
; D2.L - dest key ID
; A2.L - vcb pointer
; A4.L - fidSwapVars pointer (pointer to variable storage)
;
; Output: D0 - errors
; All others - unchanged
;_________________________________________________________________________________
MvRegs reg D1-D7/A0-A1/A3-A4
MoveExtents:
extSize EQU lenxkr+lenxdr
;
; do some initial stuff
;
MOVE.L (SP)+,-(A6) ; save return address on A6 stack
MOVEM.L MvRegs, -(A6); save registers
MOVE.L D1, D6 ; save src ID for later
MOVE.L D2, D7 ; save dest ID for later
;
; collect the extent records
;
LEA tempKeyOff(A4), A0 ; get some key storage
MOVE.B #lenxkr-1, xkrKeyLen(A0)
CLR.B xkrFkType(A0) ; clear the fork
MOVE.L D6, xkrFNum(A0) ; set the key's ID
CLR.W xkrFABN(A0) ; clear the FABN
@MvNextBatch MOVE.W VCBXTRef(A2), D0 ; extents file refnum
MOVEQ #0, D2 ; no hint
jsrROM ROMBTSEARCH ; search BTree for extent record. ex<SM1><Sys7.1> Put back "ROM"
CMP.W #BtNotFound, D0 ; it won't exist the first time thru the loop
BEQ.S @MvGoOn
TST.W D0
BNE @MvExit ; must be ioError
MOVEQ #1, D1 ; second or later batch has to skip over last key searched
BRA.S @MvGoOn1
@MvGoOn MOVEQ #0, D1 ; get what it's pointing at
@MvGoOn1 MOVEQ #numExtToCache-1, D3 ; set up counter
LEA extOff(A4), A3 ; extent ptr
@MvNextRec MOVE.W VCBXTRef(A2), D0 ; extents file refnum
jsrROM ROMBTGETRECORD ; this is the first record we want for this file ex<SM1><Sys7.1> Put back "ROM"
CMP.W #BtNotFound, D0 ; if xkrFNum(A0) is cleared on this error, then this test is bogus!
BEQ.S @MvNoMoreExts
TST.W D0
BNE.S @MvExit ; must be ioError
CMP.L xkrFNum(A0), D6
BNE.S @MvNoMoreExts ; whoops, no more of this file's extents
BSR CopyExtentInfo ; copy the key and data to storage
MOVEQ #1, D1 ; tell it you want the next record
DBRA D3, @MvNextRec
;
; edit each extent key, and reinsert each extent record in the extent file
;
@MvNoMoreExts
CMP.W #numExtToCache-1, D3 ; did we do anything?
BEQ.S @MvGoDelete ; nope, we're done collecting and inserting
MOVEQ #numExtToCache-2, D0 ; -1 for loop counting, -1 cause counter had a DBRA
SUB.W D3, D0 ; number of valid extents (-1) for looping
MOVE.W D0, D3
SWAP D3 ; save for later
MOVE.W D0, D3
LEA extOff(A4), A0 ; pointer to key
MOVEA.L A0, A1
ADDQ #lenxkr, A1 ; pointer to data
MOVEQ #lenxdr, D1 ; data length
@2 MOVE.L D7, xkrFNum(A0) ; change only the id in the key to dest ID
MOVE.W VCBXTRef(A2), D0 ; extent file's refnum
jsrROM ROMBTINSERT ; insert in btree ex<SM1><Sys7.1> Put back "ROM"
BNE.S @MvBTInsertErr
ADDA.W #extSize, A0
ADDA.W #extSize, A1
DBRA D3, @2
;
; okay, done with this batch, go get the next set of extent records
;
SWAP D3 ; check how many were cached
CMP.W #numExtToCache-1, D3
BNE.S @MvGoDelete ; since it stopped early, must not be anymore
SUBA #extSize, A0 ; get the last key cached ...
MOVE.L D6, xkrFNum(A0) ; change it back to what it was before the insert code...
BRA.S @MvNextBatch ; ... and go search again
;
; we are done with all the inserts. Now go and delete the old entries.
;
@MvGoDelete MOVE.L D6, D0 ; srcID
BSR DeleteExts
BRA.S @MvExit
;
; handle errors
;
@MvBTInsertErr
CMP.W #BTExists, D0 ; BUG!
BNE.S @MvExit ; let the other errors go on up
MOVE.W #cmbadnews, D0
;
; cleanup and return
;
@MvExit MOVEM.L (A6)+, MvRegs ; restore regs
MOVE.L (A6)+,-(SP) ; put return address back on stack
TST.W D0 ; set condition codes
RTS
ENDP
END