mac-rom/Toolbox/AliasMgr/FolderMgr.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

2154 lines
86 KiB
Plaintext

;
; File: FolderMgr.a
;
; Contains: Folder Manager
;
; Written by: Darin Adler
;
; Copyright: © 1989-1992 by Apple Computer, Inc. All rights reserved.
;
; This file is used in these builds: BigBang
;
; Change History (most recent first):
;
; <SM4> 10/29/92 SWC Backed out <SM3> and fixed the INCLUDE filenames.
; <SM3> 10/28/92 SWC Changed the INCLUDEs to a LOAD of StandardEqu.d.
; <SM2> 10/22/92 CSS Change short branches to word branches.
; <24> 6/12/91 LN added #include 'SysPrivateEqu.a'
; <23> 4/16/91 PP sad,#??????:After making _LockRng call for Network Trash, return
; errors ioErr and paramErr (for unimplemented afp _LockRng) to
; the caller.
; <22> 4/7/91 PP csd,#ich-FoldMgr-0002:When reassigning and cleaning out a
; previous Trash Can folder inside a Network Trash Folder, ignore
; file locked error.
; <21> 3/22/91 PP sad,#85189:On a client, always use GetNetTrashID if a net trash
; folder is to be created. Also, if Net Trash Folder SetCatInfo
; fails, pass the error.
; <20> 3/19/91 PP dba,#WS RRE-290:Do locking of Desktop and Trash folders
; correctly after they are found.
; <19> 2/11/91 PP dba,#DCL-274:Make non network trash folders visible. Also, all
; trash folders get locked after they are found if the volume
; supports locking.
; <18> 1/14/91 PP (sad) Add GetFolderName.
; <17> 12/14/90 PP (sad) Do not lock special folders except for Desktop Folder for
; FileShare case. Reorganized hard-wired strings for internal
; names.
; <16> 8/30/90 PP gestaltFolderMgrAttr is gestaltFindFolderAttr.
; gestaltFolderMgrPresent is gestaltFindFolderPresent.
; <15> 8/6/90 PP Remove obsolete code for making old desktop visible. Do not set
; folder manager created bit in FndrInfo since it is not used by
; the Finder.
; <14> 7/19/90 PP Before getting fld# resource, make sure that SetResLoad(TRUE) is
; done in all cases.
; <13> 7/16/90 PP Set attributes of Network Trash folder before giving away the
; ownership to administrator.
; <12> 7/11/90 PP If error during TrashCan creation, close the UsageMap file.
; <11> 7/2/90 PP Integrate new shared trash folder scheme for servers. Algorithm
; is as defined by FileShare people (Pat Dirks).
; Desktop folder is visible again.
; 6/14/90 PWD Integrated new network trash algorithm. Changed to include separate
; cache for things to be remembered for all time, such as trash folder IDs.
; <10> 6/5/90 PP Set the invisible bit in FinderInfo for Desktop Folder and all
; Trash Folders.
; <9> 5/16/90 PP Set bit 11 of FinderFlags for locked folders created by
; FolderMgr. This lets Finder know that FolderMgr created that
; folder.
; <8> 5/7/90 JSM Change BSR's to JSR's in preparation for becoming a linked
; patch.
; <7> 5/3/90 PP Fix bug related to passing cleared dirID field to HGetVInfo. If
; we don't, it may affect current working directories. Change
; "Temporary Folder" to "Temporary Items".
; <6> 3/20/90 PP Support Temporary folder just like Trash or Desktop folder.
; <5> 2/6/90 PP Open up Trash Folder privileges when it is shared through
; Personal AppleShare. Check Desktop folder lock when sharing.
; <4> 2/4/90 dba move some equates to SysEqu.a
; <3> 1/22/90 PP Ignore cached error if createFolder is true.
; <2.1> 11/17/89 dba changed names of desktop folder and trash folder again,
; invisible characters were a bad idea
; <2.0> 10/17/89 prp Fixed bug in caching. Guest login on AppleShare volumes creates
; correct Trash folder.
; <1.9> 10/13/89 prp Bug fix related to Trash folder privileges. Bug fix related to
; getting volume attributes correctly. Also, look for System
; Folder dirID in fndrInfo instead of System File FCB.
; 10/13/89 prp fullPrivileges constant had bits reversed. Bug in loading disk
; attributes. Get SystemFolder dirID from fndrInfo and not FCB.
; Set loginName Trash Folder privileges correctly.
; <1.8> 10/4/89 dba fix bug by correcting ioObjNamePtr equate
; <1.7> 9/26/89 prp Add support for Installer people. There is a routine
; FindFolderInternal for them. Fix bug related to branching to
; CreateNewEntry instead of NewDiskAndFolder
; 9/26/89 prp FindFolderInternal done for installer people, Fix bug related to
; branching to NewDiskAndFolder instead CreateNewEntry
; <1.6> 9/18/89 prp 'Trash' and 'Desktop' folders have ascii <cr> (decimal 13) in
; front of their names instead of ascii 1 to make them work better
; with different fonts.
; <1.5> 8/16/89 prp Fix bugs related to CacheHandle in EnterFolderInCache.
; <1.4> 8/15/89 prp The CacheHandle should be allocated in System Heap.
; 8/15/89 prp Allocate initial Cache empty handle in System heap
; <1.3> 8/11/89 prp Restore param blk in a0 after calling FindFolderInCache.
; <1.2> 7/17/89 dba changed error handling a big; got rid of extraneous system
; folder check; switched from ÒAny UserÓ to ÒAdministratorÓ for
; owner of shared trash
; <1.1> 6/28/89 dba use ExpandMem instead of low-memory global; make some changes
; from code review
; 6/24/89 dba fix register bug, add comments
; <1.0> 6/2/89 prp Initial release. Folder Manager now part of Alias Manager.
; 6/2/89 dba roll into system source (change error code)
; 5/20/89 dba prepare for Finder
; 5/6/89 dba new today
;
; To Do:
; check to make sure that no one uses $FFFF0000 and higher dirIDs
; consider getting rid of Òwhere to empty trashÓ as follows:
; check the Òsuper-userÓ bit on the result of GetVolParms or GetDirAccess
; if the user is a Òsuper-userÓ, then give the root trash folder
; otherwise, use the login-name folder scheme
;
; Strategy:
;
; The Folder Manager is a single call, FindFolder, that defines a number of folder specialties.
; Examples of folder specialties are: system, desktop, trash, settings, temporary, communications,
; etc. There can be one folder of each specialty per volume. These folders fall into two major
; categories.
;
; The first category is for user-visible folders. These have different names in various countries,
; and are specified by a resource in the system file. The resource contains a list of folder
; specialties (long words) and folder names (strings). Currently, all user visible folders are in
; the system folder and are located by name. The resource in the system file contains a version
; field, so new kinds of user-visible folders can be added in the future. Note that when looking
; for user-visible folders on a disk, the Folder Manager uses the system file on that disk, rather
; than the current active system file.
;
; The second category is for special folders. These currently consist of the system folder, the
; desktop folder, and the trash folder. These folders are not visible to the user, and are not
; located by internationalizable names. The system folder is located by its dirID, which is
; stored in the FinderÕs volume information. The desktop and trash folders are located by their
; names, and the Finder and Standard File do not dispay the folders.
;
; If the FindFolder call is made for a volume which does not have a particular folder, the
; folder is created, if possible. (The exception to this is the system folder, which is created
; by the user when he drags a System and Finder into the same folder, or uses the Installer.)
; New folders are created with the Òname-lockedÓ attribute, since they are identified by name.
; New folders on volumes that support folder locking are also locked. Since the current
; implementation of user-visible folders is based on the system folder, disks without system
; folders, like AppleShare 1.0 and 2.0 volumes, do not have any user-visible folders. The desktop
; folder is not created on any disk that has the ÒnoDeskItemsÓ attribute.
;
; The trash folder is handled specially on volumes that have access controls (privileges).
; A ÒsharedÓ trash folder is created, which contains all of the user-specific trash folders.
; The ÒsharedÓ trash folder is readable, writable, and searchable by all users, and a trash
; folder is created within it for each login name. When the Finder empties the trash, it starts
; from this ÒsharedÓ trash folder, and moves down, emptying as much trash as privileges allow.
;
; To prevent excessive disk access, the Folder Manager maintains a cache of results from
; calls to FindFolder. If a disk is modified or its attributes change (it is unlocked), the
; cache is ignored. The cache is maintained as a purgeable handle in the system heap. The cache
; stores error results, as well as folderÕs that have been found. (It uses unusually high dirIDs
; to represent these error results internally: $FFFF0000 and up.)
; the following are a few notes about error handling (Darin and Prashant)
;pass GetFCBInfo returns error for System file
;pass HGetVInfo returns error for vRefNum passed in or system disk vRefNum
;pass GetVolParms returns an error other than paramErr
;fnfErr unknown specialty in the fld# resource
;pass GetCatInfo error while looking in system folder
;fnfErr folder not found and createFolder is false
;pass DirCreate error while creating folder
;pass Get/CatInfo error while setting info of folder
;pass HSetFLock error when locking folder
;pass Get/SetDirAccess error while setting privileges
;pass GetLogInInfo error while getting name of trash folder
;fnfErr trying to find folder on disk without system folder support
;fnfErr trying to find fodler on disk without system folder in volume header
;fnfErr trying to find desktop folder on disk without desktop support
;dupFNErr file found instead of folder
;pass HOpenResFile error opening another system file
;pass GetResource (or Get1Resource error from the fld# resource)
;fnfErr invalid fld# resource
;fnfErr unknown version in fld# resource
;fnfErr string too long in kFolderByName fld# resource
; ***
case on
print push,off
include 'SysEqu.a'
INCLUDE 'SysPrivateEqu.a'
; The following are missing from SysEqu.a:
ioLockFlg equ 0 ; file/directory locked bit in ioFlAttrib field
bFndrInvisible equ 6 ; Finder Info Invisible bit in upper byte of flags
mFNameLock equ $1000 ; rename-inhibit bit mask in file/folder flags
vMAttrib equ 2 ; attributes word
bNoDeskItems equ 20 ; bit for whether disk supports desktop
bAccessCntl equ 18 ; bit for whether disk supports privileges
bExtFSVol equ 16 ; bit for whether disk is External FS volume
bNoSysDir equ 17 ; bit for whether disk supports system folder
bHasFolderLock equ 10 ; bit for whether disk supports folder lock
include 'ToolUtils.a'
include 'SysErr.a'
include 'Traps.a'
include 'GestaltEqu.a'
include 'InternalMacros.a' ; useful macros
include 'Folders.a' ; public interface to folder manager
print pop, nogen
DEBUG default false ; debugging off by default
CACHING default true ; caching on by default
FOR_FOLDERMGR_INIT default false ; not for INIT by default
FOR_INSTALLER default false ; not for INSTALLER by default
;----------------------------------------------------------------------------------------------------
;
; The FolderMgrGlobals currently hold just two pointers: one non-purgable handle for the permanent
; cache of special, non-changing directory IDs [currently just trash folder IDs], and one purgable
; handle to cache all other calls to the folder manager. The latter can be emptied at any time and
; is invalidated when the volume's modification date changes.
;
; NOTE: The name emFolderCache for the Expanded Lo-Mem variable that now holds our global ptr is
; rather misleading: it should probably be renamed to 'FolderMgrGlobals', or some such.
FolderMgrGlobalRec record 0 ;
normalCacheHndl ds.l 1 ; handle to purgable cache
specialCacheHndl ds.l 1 ; handle to non-purgable special cache
FolderMgrGlobalSize equ * ;
endr
;----------------------------------------------------------------------------------------------------
;
; The special cache consists of a table of entries, each one identified by the volume's VRefnum.
; Associated with each VRefNum is the Trash Map file lrefNum and the Trash Folder DirID.
;
SpecialCacheEntry record 0 ;
scVRefNum ds.w 1 ; VRefNum of volume this entry is for
scTrashMapRefNum ds.w 1 ; Trash map file refNum
scTrashDirID ds.l 1 ; The trash folder DirID
SpecialCacheEntrySize equ * ;
endr
;----------------------------------------------------------------------------------------------------
; The FolderNameList resource contains an element for each folder specialty.
; The element contains the folder specialty that it is a name for, a version (0 for current version),
; a length, and data. For the first implementation, the data is the name of a folder in the
; system folder.
kFolderNameListType equ 'fld#' ; resource type and ID for the folder name list
kFolderNameListID equ 0
FolderName record 0
type ds.l 1 ; folder specialty for which this is an entry
version ds.w 1 ; type of entry (0 for folder name)
length ds.w 1 ; length of this folder name
data equ * ; the data (text for version 0)
endr
kFolderByNameVersion equ 0 ; this versionÕs data is just the name
kStringMaxLength equ 255 ; if string is too big, itÕs invalid
if CACHING then
; We put dirIDs in the cache. We also cache error results from previous lookups.
; This is the dirID range that is used for errors (the low word holds the error code).
kCachedErrorBase equ $FFFF0000 ; low word holds error code
endif
;--------------------------------------------------
InternalNames proc
; These are names of folders used internally by the Folder Manager.
; They are hard-wired here, and not internationalized, so that you can mix scripts
; and hard disks and file servers and always use the same folders for the files on
; the desktop in the trash and for the guest trash. This is acceptable, because the
; names should only be visible to programmers and to users who use 6.X systems on a
; disk that has been used with 7.X previously.
; The Desktop Folder has to be named ÒDesktop FolderÓ instead of ÒDesktopÓ because that
; name is already taken for the resource file containing the resource-based desktop file.
string pascal
entry DesktopFolderName, TrashFolderName, TemporaryFolderName, NetTrashFolderName, NetTrashPrefix, TrashMapName
DesktopFolderName dc.b 'Desktop Folder'
align
TrashFolderName dc.b 'Trash'
align
TemporaryFolderName dc.b 'Temporary Items'
align
NetTrashFolderName dc.b 'Network Trash Folder'
align
NetTrashPrefix dc.b 'Trash Can #' ; Prefix for each trash can name
align
TrashMapName: dc.b 'Trash Can Usage Map' ; Name of trashcan arbitration file
align
endproc
;----------------------------------------------------------------------------------------------------
; PROCEDURE InitFolderManager;
InitFolderManager proc export
if FOR_FOLDERMGR_INIT then
export FolderMgrGlobalPtr
endif
with FolderMgrGlobalRec, SpecialCacheEntry
linkSave
if FOR_FOLDERMGR_INIT then
lea FolderMgrGlobalPtr,a1 ; if already allocated, get out
tst.l (a1)
bnz.s @NoSpecialCache
endif
moveq #FolderMgrGlobalSize, d0 ;
_NewPtr ,sys,clear ; Allocate a block of sysheap memory to hold folder mgr globals
if FOR_FOLDERMGR_INIT then
lea FolderMgrGlobalPtr,a1
move.l a0,(a1)
else
move.l ExpandMem,a1
move.l a0,ExpandMemRec.emFolderCache(a1)
endif
move.l a0, a1 ; save the ptr to our globals
_NewEmptyHandle ,sys ; get a handle for the normal disposable cache
move.l a0, normalCacheHndl(a1) ; save it off in our globals
moveq #SpecialCacheEntrySize*5, d0 ; Try to grab space for 5 shared volumes
_NewHandle ,sys,clear ; Get non-purgable handle for our special, permanent cache
bne.s @NoSpecialCache ; We're in trouble here...
move.l a0, specialCacheHndl(a1) ; save it off in our globals
@NoSpecialCache:
; Note that we do not do error checking here. Instead we check for a
; nil handle whenever we use it. Since the cache handle is normally
; kept purgeable, we start it out empty, as if it was just purged.
restoreUnlinkReturn
endwith
if FOR_FOLDERMGR_INIT then
FolderMgrGlobalPtr dc.l 0
endif
endproc
;===============================================================================
;
; PROCEDURE gestaltFolderMgr(); - Folder manager gestalt function
;
; FUNCTION gestaltFolderMgr (
; gestaltSelector: OSType; = PACKED ARRAY [1..4] OF CHAR;
; VAR gestaltResult: Longint;
; ): OSErr; = Integer;
;
;===============================================================================
gestaltFolderMgr proc export
resultsStackFrame
error ds.w 1 ; the resulting error code
parametersStackFrame
gestaltSelector ds.l 1 ; the selector type
gestaltResult ds.l 1 ; returned result
endStackFrame
linkSave
move.w #gestaltUndefSelectorErr,d0 ; assume bad selector was passed
clr.l d1 ; prepare for return result
cmp.l #gestaltFindFolderAttr,gestaltSelector(a6)
bne.s @done ; not the right selector
bset.l #gestaltFindFolderPresent,d1 ; set the presence bit
clr.w d0 ; no error
@done
move.l gestaltResult(a6),a0 ; load address of result
move.l d1,(a0) ; return the computed result
move.w d0,error(a6); ; return error code
restoreUnlinkReturn
ENDPROC
;----------------------------------------------------------------------------------------------------
; FUNCTION FindFolder(vRefNum: INTEGER; folderType: OSType; createFolder: BOOLEAN;
; VAR foundVRefNum: INTEGER; VAR foundDirID: LONGINT): OSErr;
;
; This routine is a wrapper around an internal routine that expects a resource handle as an
; additional parameter. This routine calls it with a nil resource handle.
;
FindFolder proc export
import FindFolderInternal
resultsStackFrame
error ds.w 1 ; the resulting error code
parametersStackFrame
vRefNum ds.w 1 ; the vRefNum of the disk we want to look at
which ds.l 1 ; which folder we are interested in
createFolder ds.w 1 ; should we create the folder if not found
pFoundVRefNum ds.l 1 ; the vRefNum of the folder we found
pFoundDirID ds.l 1 ; the dirID of the folder we found
endStackFrame
linkSave
if FOR_FOLDERMGR_INIT then
jsr InitFolderManager ; do initialization of global pointers
endif
rsrv.w ; room for return code
push.w vRefNum(a6) ; push all params for calling internal routine
push.l which(a6)
push.w createFolder(a6)
push.l pFoundVRefNum(a6)
push.l pFoundDirID(a6)
push.l #nil ; 'fld#' resource is not loaded
jsr FindFolderInternal
pop.w d0 ; get return code
move.w d0,error(a6) ; return error code
restoreUnlinkReturn
endproc
;----------------------------------------------------------------------------------------------------
; FUNCTION GetFolderName (vRefNum: INTEGER; folderType: OSType;
; VAR foundVRefNum: INTEGER; VAR name: StringPtr): OSErr;
;
; This routine returns the name of a special folder. Currently, implemented only for
; hard-wired internal names
;
GetFolderName proc export
resultsStackFrame
error ds.w 1 ; the resulting error code
parametersStackFrame
vRefNum ds.w 1 ; the vRefNum of the disk we want to look at
whichFolder ds.l 1 ; which folder we are interested in
pFoundVRefNum ds.l 1 ; the vRefNum of the folder we found
pName ds.l 1 ; the pointer to buffer where name is returned
endStackFrame
folderType equ d1 ; register for folder specialty
linkSave
move.l whichFolder(a6),folderType ; get the folder specialty we return name for
cmp.l #kDesktopFolderType,folderType ; is it the desktop folder?
beq.s @DesktopFolder ; yes, the desktop folder
cmp.l #kWhereToEmptyTrashFolderType,folderType ; is it the network trash folder?
beq.s @NetTrashFolder ; yes, the network trash folder
cmp.l #kTrashFolderType,folderType ; is it the trash folder?
beq.s @TrashFolder ; yes, the trash folder
cmp.l #kTemporaryFolderType,folderType; is it the temporary folder?
beq.s @TemporaryFolder ; yes, the temporary folder
moveq #paramErr,d0 ; Unrecognized folder type
bra.s @done
@DesktopFolder
lea DesktopFolderName,a0 ; desktop folder name
bra.s @fillName
@NetTrashFolder
lea NetTrashFolderName,a0 ; Network trash folder name
bra.s @fillName
@TrashFolder
lea TrashFolderName,a0 ; trash folder name
bra.s @fillName
@TemporaryFolder
lea TemporaryFolderName,a0 ; temporary folder name
@fillName
move.l pName(a6),a1 ; get return name string pointer
moveq #0,d0 ; Clean upper 3 bytes
move.b (a0),d0 ; Pick up string length
addq.l #1, d0 ; Add 1 for the length byte itself
_BlockMove ; Copy the name
move.l pFoundVRefNum(a6),a0 ; return the vRefNum
move.w vRefNum(a6),(a0) ;
moveq #noErr,d0 ; no error
@done
move.w d0,error(a6) ; return error code
restoreUnlinkReturn
endproc
;----------------------------------------------------------------------------------------------------
; FUNCTION FindFolderInternal(vRefNum: INTEGER; folderType: OSType; createFolder: BOOLEAN;
; VAR foundVRefNum: INTEGER; VAR foundDirID: LONGINT; resHandle : Handle): OSErr;
;
; Find the folder of the specified type or create it if it doesnÕt already exist.
; The vRefNum and dirID will be cached in a handle maintained in the system heap.
;
; The cache is kept so that no disk access will occur if you ask for the same folder
; many times in a row. The cache is kept as long as the modification date and attributes
; of a disk remain the same. If the modification date changes, folder may have moved or
; been deleted. If the attributes change, a folder my be created on a disk that was
; previously locked.
;
; The resHandle is a handle to loaded resource of type 'fld#' and id 0. If it is nil, the
; routine loads the resource from the system file.
FindFolderInternal proc export
if FOR_INSTALLER then
import HOPENRESFILE
endif
if CACHING then
import FindFolderInCache,EnterFolderInCache
endif
import FindSpecialCacheEntry,AddSpecialCacheEntry
import GetNetTrashID,GetTrashMapRefnum,GetTrashCanID
; if FOR_FOLDERMGR_INIT then
; import INITFolderNameListHandle
; endif
kFileNotOpen equ -1 ; a refNum representing a file that isnÕt open
resultsStackFrame
error ds.w 1 ; the resulting error code
parametersStackFrame
vRefNum ds.w 1 ; the vRefNum of the disk we want to look at
which ds.l 1 ; which folder we are interested in
createFolder ds.w 1 ; should we create the folder if not found
pFoundVRefNum ds.l 1 ; the vRefNum of the folder we found
pFoundDirID ds.l 1 ; the dirID of the folder we found
resHandle ds.l 1 ; the handle to 'fld#' resource
localsStackFrame
diskPB ds.b ioHVQElSize ; the disk parameter block
diskPBVolParms ds.b ioActCount+4 ; the disk parameter block for GetVolParms
diskVolParms ds.b vMAttrib + 4 ; room for attributes
folderPB ds.b ioHFQElSiz ; the folder parameter block
loginName ds.b 32 ; room for the log-in name
savedResFile ds.w 1 ; room for the saved current res file refNum
savedNetTrashID ds.l 1 ; room for the saved NetWork Trash dirID
savedTrashMapRefNum ds.w 1 ; room for the saved NetWork TrashMapUsage ref num
endStackFrame
assert ioFCBQElSize <= ioHVQElSize ; make sure both fit in diskPB
assert ioHQElSize <= ioHFQElSiz ; make sure both fit in folderPB
systemVRefNum equ d3 ; register for system disk vRefNum
cookedVRefNum equ d4 ; register for vRefNum
dirID equ d5 ; register for dirID
resourceFileRefNum equ d6 ; register for resource file refNum
diskAttributes equ d7 ; register for disk attributes
folderType equ a2 ; register for folder specialty
namesResource equ systemVRefNum ; register for resource containing names
linkSave d2-d7/a2
moveq #kFileNotOpen,resourceFileRefNum ; no resource file open yet
move.l which(a6),folderType ; get the folder specialty we must find
rsrv.w ; room for returned refNum
_CurResFile ; get the current ResFile refNum
pop.w savedResFile(a6) ; save it for restoring later on
push.w #0 ; System file refNum
_UseResFile ; Make System file current
lea diskPB(a6),a0 ; point to the parameter block
;--------------------------------------------------
; Here we get information about the vRefNum passed to us. We also get the vRefNum of the system
; disk, since we need it know to handle the kOnSystemDisk case, and will need it later in most
; cases. Also note that the calls we make, GetFCBInfo and HGetVInfo, should be fairly quick, since
; they simply look for the FCB and VCB respectively.
GetSystemFileInfo
clr.l ioFileName(a0) ; not interested in file name
move.w SysMap,ioRefNum(a0) ; the system fileÕs refNum
clr.w ioFCBIndx(a0) ; get file by refNum
moveq #fsRtDirID,d0 ; initialize in case of an MFS disk
move.l d0,ioFCBParID(a0) ; (MFS FCBs do not have parIDs in them)
_GetFCBInfo ; get the FCB
bnz GotError ; it is an error
move.w ioFCBVRefNum(a0),systemVRefNum ; get the system vRefNum
GetDiskInfo
move.w vRefNum(a6),d0 ; put vRefNum in parameter block
cmp.w #kOnSystemDisk,d0 ; this means the system folder
bne.s @notSystemDisk ; if they pass us this, itÕs the system disk
move.w systemVRefNum,d0 ; get the system disk vRefNum
@notSystemDisk
move.w d0,ioVRefNum(a0) ; put the vRefNum in the parameter block
assert ioVolIndex = ioFCBIndx ; get the information by vRefNum (set up above)
clr.l ioVFndrInfo(a0) ; initialize in case of an MFS disk
; If we don't init dirID field, it causes an HFS bug related to changing working
; directory number on us.
clr.l ioDirID(a0) ; work-around HFS bug
_HGetVInfo ; get the real vRefNum
bnz GotError ; it is an error?
move.w ioVRefNum(a0),cookedVRefNum ; get the vRefNum
;--------------------------------------------------
; Search the cache to see if this folder specialty has been entered into the cache for this disk.
; If we find a dirID here, go directly to the end of the routine.
if CACHING then
rsrv.l ; make room for the return value
push.l a0 ; pass the disk information
push.l folderType ; pass which folder we want
jsr FindFolderInCache ; found the folder in the cache
lea diskPB(a6),a0 ; restore the parameter block
pop.l d0 ; did we get a dirID?
bz.s @notInCache ; no, itÕs not in the cache
; If we got a cache error and if the createFolder bit is set, ignore the cache and try to create the
; folder.
cmp.l #kCachedErrorBase,d0 ; is the folder an error?
blo.s @notCachedError
tst.b createFolder(a6) ; are we supposed to create the folder?
bnz.s @notInCache ; if yes, ignore the cache and create the folder
@notCachedError
move.l d0,dirID ; return that dirID
bra GotFolderFromCache ; we got the cache item
@notInCache
endif
;--------------------------------------------------
; Since we didnÕt find the folder in the cache, we need the list of disk attributes.
; This is used when finding the system folder, creating new folders, and determining whether
; there should be a desktop folder.
lea diskPBVolParms(a6),a0 ; point buffer for GetVolParms call
clr.l ioFileName(a0) ; give vRefNum instead of fileName
move.w cookedVRefNum,ioVRefNum(a0) ; for this disk
lea diskVolParms(a6),a1 ; point buffer at parameters
move.l a1,ioBuffer(a0) ; read the parameters in here
moveq #vMAttrib+4,d0 ; read just enough to get the attributes
move.l d0,ioReqCount(a0) ; read in the attributes
_GetVolParms ; get the disk parameters
bz.s @success
cmp.w #paramErr,d0 ; was the error just a lack of GetVolParms?
bne GotError
moveq #0,diskAttributes ; all attributes are zero by default
bra.s @gotParms
@success
move.l diskVolParms+vMAttrib(a6),diskAttributes ; got all the attributes
@gotParms
;--------------------------------------------------
; Now, we check for the special folders that are not inside the system folder.
;
cmp.l #kDesktopFolderType,folderType ; is it the desktop folder?
beq DesktopFolder ; yes, the desktop folder
cmp.l #kWhereToEmptyTrashFolderType,folderType ; is it the other trash folder?
beq TrashFolder ; yes, the trash folder
cmp.l #kTrashFolderType,folderType ; is it the trash folder?
beq TrashFolder ; yes, the trash folder
cmp.l #kTemporaryFolderType,folderType; is it the temporary folder?
beq TemporaryFolder ; yes, the temporary folder
;--------------------------------------------------
; We still have to get the System Folder dirID.
btst.l #bNoSysDir,diskAttributes ; is the system folder supported on this disk
bnz.s NotFoundError ; no system folder? no others either
move.l diskPB+ioVFndrInfo(a6),dirID ; get the system folder on this disk
bnz.s FoundSystemFolder ; no system folder? no others either
NotFoundError
moveq #fnfErr,d0 ; folder not found
bra GotErrorForCache ; go report that error
;--------------------------------------------------
; We have found the dirID of the system folder. If that is the folder we were looking for, then
; we can return immediately.
FoundSystemFolder
cmp.l #kSystemFolderType,folderType ; is it the system folder?
beq GotFolderForCache ; return with the system dirID
;--------------------------------------------------
; We now have identified the disk and the system folder that we wish to find a folder specialty
; on/in. We will now open the system file on that disk and extract the folder name list resource
; from it. Note that the name of the system file (in low memory) is the same across international
; and script boundaries. If we are looking on the current System disk, we do not open the
; System file. We have already made it the current one for getting resources.
; namesResource and systemVRefNum are same registers. Do not optimize following test.
tst.l resHandle(a6) ; Was 'fld#' resource already loaded?
bz.s @GetResourceFromSystem ; no, get it from the System file
move.l resHandle(a6),namesResource ; set up resource handle correctly
bra.s GotResHandle ; we have the handle to the loaded resource
@GetResourceFromSystem
push.b #true ; always load 'fld#' resource in memory
_SetResLoad
cmp.w cookedVRefNum,systemVRefNum ; is it the system disk?
beq.s GetFolderList ; It is already open and current
push.b #false ; donÕt load any resources from the system file
_SetResLoad ; while opening it
rsrv.w ; make room for result refNum
push.w cookedVRefNum ; disk
push.l dirID ; folder
pea SysResName ; system file name from low memory
push.b #fsRdPerm ; read-only
if FOR_INSTALLER then
jsr HOPENRESFILE ; open the system, installer does not have trap
else
_HOpenResFile ; open the system
endif
pop.w resourceFileRefNum ; get system file refNum
rsrv.w ; make room for error
_ResError ; get error
push.b #true ; restore resource loading
_SetResLoad
pop.w d0 ; pop that error from the resource manager
bnz GotErrorForCache
GetFolderList
rsrv.l ; room for resource handle
push.l #kFolderNameListType ; get folder list resource
push.w #kFolderNameListID
_Get1Resource ; get resource from other file
pop.l namesResource ; get handle to resource
bz GotResourceErrorForCache
;--------------------------------------------------
; Now that we have the resource, we can search the resource for the folder specialty we desire.
; We keep searching until we reach the end of the resource.
GotResHandle
move.l namesResource,a0 ; get the size of this handle
_GetHandleSize
move.l (a0),a0 ; dereference the handle
lea -FolderName.data(a0,d0.l),a1 ; point to last possible entry
@next
cmp.l a1,a0 ; are we at the end of the resource?
bhi.s NotFoundError ; yes, return an error
moveq #0,d1 ; get the size of this element
move.w FolderName.length(a0),d1 ; in a long word
cmp.l FolderName.type(a0),folderType ; is this the right kind of folder?
beq.s FolderNameInResource
addq.l #1,d1 ; round size up to an even number
bclr #0,d1
lea FolderName.data(a0,d1.l),a0 ; advance to next folder name
bra.s @next
;--------------------------------------------------
; We found a folder name in the folder name list resource. Make sure that it is a version 0
; name (a sub-folder of the system folder): we havenÕt defined any others yet, but folder
; name lists might be from a newer system that has new ones. Once we are sure it is a
; version 0 name, get the pascal string name of the sub-folder, and go on. If we notice that
; the fld# resouce is damaged, we return a Ònot foundÓ error
FolderNameInResource
sub.l d1,a1 ; subtract the size of the data
cmp.l a1,a0 ; check if the data fits
bhi NotFoundError ; no, we are done
cmp.w #kFolderByNameVersion,FolderName.version(a0) ; this version?
bne NotFoundError ; no, must be unknown version
cmp.w #kStringMaxLength,d1 ; is the data short enough?
bhi NotFoundError ; too long, get out of here
lea FolderName.length+1(a0),a1 ; point at the string
bra FolderNameInA1
;--------------------------------------------------
; Here is where the ÒrootÓ folders rejoin the main code path. The main difference of these
; folders is that they start in the root, and have names that do not come from resources.
; These are folders like Trash, Desktop and Temporary. Trash folders on shared volumes are
; handled very differently. **** Describe the complete algorithm here!!
;
; **PWD** Somewhere here we have to decide whether this volume is shared and, if so, whether
; to create a new trash folder (we wouldn't be here if a trash folder had been created already):
;
TrashFolder
lea TrashFolderName,a1 ; trash folder name
btst.l #bAccessCntl,diskAttributes ; does this disk support privileges?
bz FolderInRoot ; otherwise, nothing special about this trash
; following test differentiates a server from the client
btst.l #bExtFSVol,diskAttributes ; Is this disk external file system?
bz FolderInRoot ; otherwise, nothing special about trash
cmp.l #kWhereToEmptyTrashFolderType,folderType ; is it the other trash folder?
bne.s @GetClientTrash ; no, get the client trash folder within NetTrash
; load upper level NetTrash folder name and handle it like any other special folder if not creating it
lea NetTrashFolderName,a1 ; NetTrash folder name
tst.b createFolder(a6) ; are we supposed to create Network Trash folder?
bz FolderInRoot ; if not, treat it just like any other folder
move.w cookedVRefNum,d0 ; set correct vRefNum for getting NetTrashID
jsr GetNetTrashID ; upper level NetTrash folder ID
bne GotErrorForCache ; if not, go enter it into the cache
move.l d1,dirID ; got the Network Trash Folder dirID
bra GotFolderForCache ;
; Find the NetWork Trash in special cache. If not there, may be the upper level trash as well as
; the other trash can within it and the trash usage map file need to be created.
@GetClientTrash
with SpecialCacheEntry
rsrv.l ; make room for ptr to cache entry
push.w cookedVRefNum ; pass vRefNum
jsr FindSpecialCacheEntry ; search the special cache
pop.l d0 ; check search result
beq.s @trashNotInSpecialCache ; if search fails, try creating a new trash folder
movea.l d0, a0 ; point to cache entry for this volume
move.l scTrashDirID(a0), dirID ; pick up the trash folder dirID
bra GotFolderForCache ; got the Trash dirID
endwith
@trashNotInSpecialCache
move.w #fnfErr,d0 ; assume createFolder bit is not set
tst.b createFolder(a6) ; are we supposed to create the folder?
bz GotErrorForCache ; if not, go enter it into the cache
move.w cookedVRefNum,d0 ; set correct vRefNum for getting NetTrashID
jsr GetNetTrashID ; upper level NetTrash folder ID
bne GotErrorForCache ; if not, go enter it into the cache
move.l d1,savedNetTrashID(a6) ; save it for a while
pea diskPB(a6) ; pass the disk information
push.l #kWhereToEmptyTrashFolderType ; pass the type
push.l d1 ; pass the dirID
jsr EnterFolderInCache ; call the cache to enter it in
move.w cookedVRefNum,d0 ; set correct vRefNum for getting TrashUsageMap refNum
move.l savedNetTrashID(a6),d1 ; pass Network Trash Folder dirID
jsr GetTrashMapRefnum ; Trash Map usage refNum
bne GotErrorForCache ; if not, go enter it into the cache
move.w d1,savedTrashMapRefNum(a6) ; save it for a while
move.w cookedVRefNum,d0 ; set correct vRefNum for getting Trash Can ID
move.l savedNetTrashID(a6),d1 ; pass Network Trash Folder dirID
move.w savedTrashMapRefNum(a6),d2 ; pass TrashMapUsage refNum to GetTrashCanID call
jsr GetTrashCanID ; real Trash can ID
bne.s @TrashCanError ; if not, go enter it into the cache
move.l d1,dirID ; also, remember it for purgeable cache
push.w cookedVRefNum ; pass the vRefNum
push.w savedTrashMapRefNum(a6) ; pass NetWork Trash map file refNum
push.l dirID ; this is the real Trash can dirID
jsr AddSpecialCacheEntry ; remember it in non-purgeable cache
bra GotFolderForCache ; we have created the trash can
@TrashCanError
move.w d0,d2 ; save the TrashCan creation error for a while
lea folderPB(a6),a0 ; get ready for calls
move.w savedTrashMapRefNum(a6), ioRefNum(a0); Set up trash map file refnum
_Close ; close Trash Map file
move.w d2,d0 ; TrashCan creation error
bra.s GotErrorForCache ; if not, go enter it into the cache
DesktopFolder
btst.l #bNoDeskItems,diskAttributes ; no desktop?
bnz NotFoundError ; if not, return an error
lea DesktopFolderName,a1 ; desktop folder name
bra.s FolderInRoot
TemporaryFolder
lea TemporaryFolderName,a1 ; temporary folder name
FolderInRoot
moveq #fsRtDirID,dirID ; look for these in the root
;--------------------------------------------------
; Now we have the name of the folder in a1 and its parent folderÕs dirID in ÒdirIDÓ.
; Find the folder by that name. If it is a file instead of a folder, we fail with a
; Òduplicate file nameÓ error.
FolderNameInA1
lea folderPB(a6),a0 ; get ready for calls
move.l a1,ioFileName(a0) ; look for this name
move.w cookedVRefNum,ioVRefNum(a0) ; on this disk
clr.w ioFDirIndex(a0) ; by vRefNum and dirID
move.l dirID,ioDrDirID(a0) ; in this folder
_GetCatInfo
cmp.w #fnfErr,d0 ; is there a folder by that name?
beq.s NewFolderNeeded
tst.w d0 ; another error?
bnz.s GotErrorForCache
move.l ioDrDirID(a0),dirID ; got the dirID!
btst.b #ioDirFlg,ioFlAttrib(a0) ; check if it is a folder
bnz.s FoundFolder ; it is!
moveq #dupFNErr,d0 ; if there is a file instead of a folder
bra.s GotErrorForCache
;--------------------------------------------------
; Here is where we come when we find an error that should be cached. If this is a
; caching version of the folder manager, we convert it info a dirID and fall into the folder
; caching case. If we are not caching we just return the error.
GotResourceErrorForCache
rsrv.w ; make room for error
_ResError ; get error
pop.w d0 ; from resource manager
bnz.s GotErrorForCache
move.w #resNotFound,d0 ; resource not found error
GotErrorForCache
if CACHING then
moveq #(kCachedErrorBase or $FFFF),dirID ; build a dirID from the error
move.w d0,dirID ; put the error in the low word
bra.s GotFolderForCache
else
bra.s GotError ; just a normal error
endif
;--------------------------------------------------
; We come here if we find that there is no folder by the right name. We now have to create the
; new folder and set its attributes appropriately. *** It may be inappropriate to set the
; attributes for a new folder, and not do the same for folders that we find for the first time. ***
NewFolderNeeded
tst.b createFolder(a6) ; are we supposed to create the folder?
bz.s GotErrorForCache ; if not, go enter it into the cache
move.l dirID,ioDrDirID(a0) ; in this folder
_DirCreate ; create the folder
bnz.s GotErrorForCache ; get out if we couldnÕt create it
move.l ioDrDirID(a0),d2 ; save away the dirID for a bit
clr.l ioFileName(a0) ; get info on the folder
move.w #-1,ioFDirIndex(a0) ; look up by dirID
_GetCatInfo ; get info about that folder
bnz.s GotErrorForCache ; get out if there was a problem
; Note that although the above GetCatInfo is done by dirID, the rest of these
; operations are done by name and parent dirID, because thatÕs the only way
; HFS lets us do them.
move.l a1,ioFileName(a0) ; set info by name
ori.w #mFNameLock,ioDrUsrWds+frFlags(a0) ; make it rename-inhibit
move.l dirID,ioDrDirID(a0) ; in this folder
_SetCatInfo ; change those bits a bit
bnz.s GotErrorForCache ; couldnÕt do it
move.l d2,dirID ; now weÕve got the dirID
;--------------------------------------------------
; Now we have the folder we are looking for. Either we created it, or it was already on the disk.
; Set Desktop Folder/Trash attributes appropriately whether it was created for first time or we found it.
; This is for reasons like Personal Appleshare sharing the folder and hence its attributes need to be set.
FoundFolder
;--------------------------------------------------
;
; Does Desktop Folder/Trash now need to be locked?
; This could happen if the disk is now allowing sharing and hence the locking need to be set correctly.
; Set the folder locking for Desktop Folder/Trash only. For example, after FileShare is
; running, we may need to set the lock so that the desktop folder cannot be moved,
; renamed or deleted by other Super user remotely.
cmp.l #kWhereToEmptyTrashFolderType,folderType ; is it the other trash folder?
beq.s @DoLocking ; yes, the root level trash folder
cmp.l #kTrashFolderType,folderType ; is it the trash folder?
beq.s @DoLocking ; all trash folders get locked if locking supported
cmp.l #kDesktopFolderType,folderType ; is it the desktop folder?
bne.s @done ; see if its lock needs to be set
@DoLocking
btst.l #bHasFolderLock,diskAttributes ; does this disk support locking folders?
bz.s @done ; no, donÕt bother trying
btst.b #ioLockFlg,ioFlAttrib(a0) ; check if it is already locked
bnz.s @done ; it is!
clr.l ioFileName(a0) ; no name, lock it by dirID
move.l dirID,ioDrDirID(a0) ; lock the folder
_HSetFLock ; lock it right here
; ignore error
@done
;--------------------------------------------------
; WeÕve got the dirID for the folder. Enter it into the folder cache.
GotFolderForCache
if CACHING then
pea diskPB(a6) ; pass the disk information
push.l folderType ; pass the type
push.l dirID ; pass the dirID
jsr EnterFolderInCache ; call the cache to enter it in
endif
;--------------------------------------------------
; We get here just after entering a folder into the cache, or just after getting a folder
; from the cache. In both cases, the dirID could be either a real dirID, or a cached error
; which is stored as a dirID greater than kCachedErrorBase.
;
if CACHING then
GotFolderFromCache
cmp.l #kCachedErrorBase,dirID ; is the folder an error?
blo.s @notError
move.w dirID,d0 ; get the error code
bra.s GotError
@notError
endif
;--------------------------------------------------
; We found the folder, return to the caller.
GotFolder
move.l pFoundVRefNum(a6),a0 ; return the vRefNum
move.w cookedVRefNum,(a0) ; save the vRefNum
move.l pFoundDirID(a6),a0 ; return the dirID
move.l dirID,(a0) ; save the dirID
moveq #noErr,d0 ; no error
;--------------------------------------------------
; We are finished, and have the error code in d0. Close the other systemÕs resource file.
GotError
move.w d0,error(a6) ; return result
cmp.w #kFileNotOpen,resourceFileRefNum ; is an alternate system file open?
beq.s @done
push.w resourceFileRefNum ; get that system file refNum
_CloseResFile
rsrv.w ; make room for error
_ResError ; get error
pop.w d0 ; from resource manager
bz.s @done
tst.w error(a6) ; is there an error yet
bnz.s @done
move.w d0,error(a6) ; no, use the CloseResFile error
@done
push.w savedResFile(a6) ; restore saved ResFile refNum
_UseResFile
restoreUnlinkReturn
endproc
;----------------------------------------------------------------------------------------------------
; Code to implement the cache starts here:
if CACHING then
;----------------------------------------------------------------------------------------------------
FolderCacheFolder record 0
type ds.l 1 ; the folderType that this is a cache for
dirID ds.l 1 ; the dirID that is stored here
endrsize
FolderCacheDisk record 0
vRefNum ds.w 1 ; the vRefNum of the disk
creationDate ds.l 1 ; creation date of the disk
attributes ds.w 1 ; saved attributes of the disk
modificationDate ds.l 1 ; last modification date of the disk
numFoldersMinusOne ds.w 1 ; number of folders cached - 1
firstFolder ds.b FolderCacheFolder ; folders to cache start here
sizeWithOneFolder equ * ; size if we have exactly one folder cached
endr
;----------------------------------------------------------------------------------------------------
; PROCEDURE EnterFolderInCache(diskPB: HParmBlkPtr; folderType: OSType; dirID: LONGINT);
;
; Add an entry to the folder cache, a handle kept in the system heap (purgeable).
;
; The folder cache keeps entries for each disk, and pairs of OSType and dirID for each
; folder specialty on each disk. The cache is kept as long as the modification date and attributes
; of a disk remain the same. Note that this means that cache entries are often thrown away.
EnterFolderInCache proc export
import FindEntryInCache
if FOR_FOLDERMGR_INIT then
import FolderMgrGlobalPtr
endif
parametersStackFrame
pDiskPB ds.l 1 ; result of HGetVolInfo on the disk
which ds.l 1 ; which folder specialty we are interested in
dirID ds.l 1 ; the dirID we want to cache
localsStackFrame
diskEntry ds.l 1 ; disk entry found by FindEntryInCache
stage ds.b FolderCacheDisk ; staging area for new disk
endStackFrame
cacheHandle equ a3 ; store the cacheHandle here
linkSave a3
if DEBUG then
tst.l dirID(a6) ; canÕt enter a 0 into the cache
bnz.s @ok
_Debugger
@ok
endif
;--------------------------------------------------
; Reconstitute the cache handle if it was purged.
with FolderMgrGlobalRec
if FOR_FOLDERMGR_INIT then
move.l FolderMgrGlobalPtr,d0
else
move.l ExpandMem,a0
move.l ExpandMemRec.emFolderCache(a0),d0 ; get cache handle
endif
bz Done ; no globals, we're doomed
movea.l d0, a0 ; set up ptr to our globals
move.l normalCacheHndl(a0), d0 ; pick up cache handle
bz Done ; no cache, we are done
endwith
move.l d0,cacheHandle ; save cacheHandle for later use
tst.l (cacheHandle) ; is the cache handle empty?
bnz.s @findEntry ; no, look for existing contents
move.l cacheHandle,a0 ; copy in a0 for the calls
moveq #0,d0 ; reallocate handle (no contents)
_ReallocHandle sys
bnz Done ; canÕt reallocate, get out
_HPurge ; keep it purgeable
bra.s NewDiskAndFolder ; we reallocated the handle
@findEntry
rsrv.l ; make room for result
push.l pDiskPB(a6) ; search on this disk
push.l which(a6) ; for this type
pea diskEntry(a6) ; and remember where the disk is
jsr FindEntryInCache ; find the entry for this folder
pop.l d0 ; get pointer to entry
bnz.s FoundEntry
;--------------------------------------------------
; We get here if there is no existing entry in the cache for this folder specialty and disk.
; Inserting a new entry is different if the disk has other entries already.
CreateNewEntry
move.l diskEntry(a6),d0 ; get entry for the disk
bnz.s NewFolderOnly
NewDiskAndFolder
; in this case, we insert a new disk and a new folder (at the beginning of the handle)
move.l pDiskPB(a6),a1 ; get parameter block pointer
move.l ioVRefNum(a1),stage.vRefNum(a6) ; put in the vRefNum
move.l ioVCrDate(a1),stage.creationDate(a6) ; put in the creation date
move.w ioVAtrb(a1),stage.attributes(a6) ; put in the attributes
move.l ioVLsMod(a1),stage.modificationDate(a6) ; put in the modification date
move.w #1-1,stage.numFoldersMinusOne(a6) ; initialize number of folders
lea stage(a6),a1 ; set up for both disk and folder
moveq #FolderCacheDisk.sizeWithOneFolder,d1 ; get size
moveq #0,d2 ; donÕt replace anything
bra.s FillInFolder
NewFolderOnly
; in this case, we insert a new folder count and a new folder over the old folder count
moveq #FolderCacheDisk.numFoldersMinusOne,d1 ; advance to proper place to insert a folder
add.l d1,d0
move.l d0,a1 ; get pointer to old entry
move.w (a1),d1 ; get old number of folders
addq.w #1,d1 ; we are about to add a folder
move.w d1,stage.numFoldersMinusOne(a6) ; the new, incremented, number of folders
sub.l (cacheHandle),d0 ; convert disk entry address to an offset
lea stage.numFoldersMinusOne(a6),a1 ; set up for folder only
assert FolderCacheDisk.numFoldersMinusOne + 2 = FolderCacheDisk.firstFolder
moveq #2 + FolderCacheFolder.size,d1 ; get size
moveq #2,d2 ; get size we are replacing
;--------------------------------------------------
; Now we have the place to insert the new entry, size to be replaced, new entry composed on the
; stack frame, pointer to the new entry, and size of the new entry. All thatÕs left it to munge
; it in.
FillInFolder
move.l which(a6),stage.firstFolder.type(a6) ; put in the folderType
move.l dirID(a6),stage.firstFolder.dirID(a6) ; put in the dirID
rsrv.l ; room for the result
push.l cacheHandle ; munge this handle
push.l d0 ; munge it in here
push.l #nil ; donÕt search, just insert
push.l d2 ; number of bytes to replace
push.l a1 ; hereÕs what you should insert
push.l d1 ; hereÕs the length of what you should insert
_Munger
free.l ; ignore the result
bra.s Done ; itÕs in, now weÕre done
;--------------------------------------------------
; If we find an entry, we simply change its dirID in place. This could happen if a cached error
; was found and the createFolder is set. In this case, the cached entry is updated to have the
; real diID which was just created.
FoundEntry
move.l d0,a0 ; fill in this entry
move.l dirID(a6),FolderCacheFolder.dirID(a0)
Done
restoreUnlinkReturn
endproc
;----------------------------------------------------------------------------------------------------
; FUNCTION FindFolderInCache(diskPB: HParmBlkPtr; folderType: OSType): LONGINT;
;
; Find an entry in the folder cache for a folder specialty on a particular disk.
; Return the entry, or if there is no entry, return 0.
FindFolderInCache proc export
import FindEntryInCache
if FOR_FOLDERMGR_INIT then
import FolderMgrGlobalPtr
endif
resultsStackFrame
dirID ds.l 1 ; the dirID we found in the cache
parametersStackFrame
pDiskPB ds.l 1 ; result of HGetVolInfo on the disk
which ds.l 1 ; which folder specialty we are interested in
endStackFrame
linkSave
rsrv.l ; make room for result
push.l pDiskPB(a6) ; search on this disk
push.l which(a6) ; for this type
push.l #nil ; no disk result desired
jsr FindEntryInCache ; find the entry for this folder
pop.l d0 ; get pointer to entry
bz.s Done ; zero means no entry
move.l d0,a0 ; get dirID from this entry
move.l FolderCacheFolder.dirID(a0),d0
Done
move.l d0,dirID(a6) ; dirID from cache
restoreUnlinkReturn
endproc
;----------------------------------------------------------------------------------------------------
; FUNCTION FindEntryInCache(diskPB: HParmBlkPtr; folderType: OSType;
; VAR disk: FolderCacheDiskPtr): FolderCacheFolderPtr;
;
; Find a cache entry for a folder. Regardless of whether the folder is found, return the
; cache entry for the whole disk too.
FindEntryInCache proc export
resultsStackFrame
entry ds.l 1 ; the entry we found in the cache
parametersStackFrame
pDiskPB ds.l 1 ; result of HGetVolInfo on the disk
which ds.l 1 ; which folder specialty we are interested in
pDiskEntry ds.l 1 ; store a pointer to the disk entry here
endStackFrame
pb equ a2 ; keep the parameter block here
cacheHandle equ a3 ; store the cacheHandle here
pDiskResult equ a4 ; pointer to the resulting disk
linkSave a2-a4
move.l pDiskPB(a6),pb ; keep PB handy
clr.l entry(a6) ; nothing is default result
move.l pDiskEntry(a6),pDiskResult ; get disk result
move.l pDiskResult,d0 ; check for NIL
bz.s @noDiskResult
clr.l (pDiskResult) ; no disk result by default
@noDiskResult
;--------------------------------------------------
; Get the cache from low-memory. Search it for an appropriate entry.
with FolderMgrGlobalRec
if FOR_FOLDERMGR_INIT then
move.l FolderMgrGlobalPtr,d0
else
move.l ExpandMem,a0
move.l ExpandMemRec.emFolderCache(a0),d0 ; get cache handle
endif
bz Done ; no globals, get out
movea.l d0, a0 ; set up pointer to our globals
move.l normalCacheHndl(a0), d0 ; pick up normal cache handle
bz Done ; no handle, get out
endwith
move.l d0,a0 ; get handle in address register, too
tst.l (a0) ; check if it is purged
bz Done ; <SM2> CSS
move.l a0,cacheHandle ; use this later
_GetHandleSize ; get the full length of the handle
move.l (a0),a0 ; dereference the handle
lea -FolderCacheDisk.firstFolder(a0,d0.l),a1 ; point to last possible entry
@next
cmp.l a1,a0 ; check if we are done yet
bhi.s Done ; yes, no entry for this disk
move.w FolderCacheDisk.numFoldersMinusOne(a0),d0 ; get the number of folders
moveq #0,d1 ; calculate size of excess folders (more than 1)
move.w d0,d1 ; which must be a long word
assert FolderCacheFolder.size = (1 << 3)
asl.l #3,d1 ; use a quick shift
moveq #FolderCacheDisk.sizeWithOneFolder,d2
add.l d2,d1 ; calculate total size of disk entry
move.w ioVRefNum(pb),d2 ; check if this is the disk
cmp.w FolderCacheDisk.vRefNum(a0),d2 ; is this the right disk?
beq.s FoundEntry
add.l d1,a0 ; advance to next disk
bra.s @next
;--------------------------------------------------
; We get here when a cache entry for a disk is found. Now we must decide if it is obsolete.
; If it is obsolete for this disk, we can munge the entry for the disk out of the cache.
FoundEntry
move.l ioVCrDate(pb),d2
cmp.l FolderCacheDisk.creationDate(a0),d2
bne.s EntryObsolete ; dates do not match
move.w ioVAtrb(pb),d2
cmp.w FolderCacheDisk.attributes(a0),d2
bne.s EntryObsolete ; attributes do not match
move.l ioVLsMod(pb),d2
cmp.l FolderCacheDisk.modificationDate(a0),d2
beq.s EntryNotObsolete ; dates do not match
EntryObsolete
rsrv.l ; room for the result
push.l cacheHandle ; munge this handle
sub.l (cacheHandle),a0 ; get offset
push.l a0 ; pass offset
push.l #nil ; donÕt search, just delete
push.l d1 ; length to delete
assert 1 <> nil
push.l #1 ; pass something non-nil so Munger will delete
push.l #0 ; if second length is 0, Munger deletes
_Munger
free.l ; ignore the result
bra.s Done
EntryNotObsolete
;--------------------------------------------------
; We get here when a cache entry for a disk is found, and is not obsolete. We can now tell
; the user where the disk was found. After that, we search for the folder specialty, and
; return it if we find it.
move.l pDiskResult,d2 ; check for NIL
bz.s @noDiskResult
move.l a0,(pDiskResult) ; store disk result here
@noDiskResult
addq.l #FolderCacheDisk.firstFolder-FolderCacheFolder.size,a0 ; where to start
move.l which(a6),d1 ; folder specialty to search for
@loop
addq.l #FolderCacheFolder.size,a0 ; advance to next folder
cmp.l FolderCacheFolder.type(a0),d1 ; is this the type?
dbeq d0,@loop ; loop until it is found
bne.s Done ; not found, we are done
move.l a0,entry(a6) ; found, we are done
;--------------------------------------------------
Done
restoreUnlinkReturn
endproc
;----------------------------------------------------------------------------------------------------
endif
;----------------------------------------------------------------------------------------------------
;****************************************************************************************************
; What follows is special code for handling SharedTrash for AppleShare (specially Personal
; AppleShare.
;****************************************************************************************************
;----------------------------------------------------------------------------------------------------
;
; FUNCTION FindSpecialCacheEntry(vRefNum: Integer): CacheEntryPtr;
;
; Find an entry in the special folder cache for a [trash] folder on a particular disk.
; Return the entry, or if there is no entry, return nil.
FindSpecialCacheEntry proc export
if FOR_FOLDERMGR_INIT then
import FolderMgrGlobalPtr
endif
resultsStackFrame
entry ds.l 1 ; the entry we found in the cache
parametersStackFrame
vRefNum ds.w 1 ; VRefNum for target volume
endStackFrame
linkSave d1
clr.l entry(a6) ; nothing is default result
; Get the special cache handle from low-memory. Search it for an appropriate entry.
with FolderMgrGlobalRec, SpecialCacheEntry
if FOR_FOLDERMGR_INIT then
move.l FolderMgrGlobalPtr,d0
else
move.l ExpandMem,a0
move.l ExpandMemRec.emFolderCache(a0),d0 ; get cache handle
endif
bz.s Done ; no globals, get out
movea.l d0, a0 ; set up pointer to our globals
move.l specialCacheHndl(a0), d0 ; pick up special cache handle
bz.s Done ; no handle, get out
move.l d0,a0 ; get handle in address register, too
;;; tst.l (a0) ; check if it is purged
;;; bz.s Done
_GetHandleSize ; get the full length of the handle
move.l (a0),a0 ; dereference the handle
CacheLoop:
subi.l #SpecialCacheEntrySize, d0 ; another entry left unseen?
blt.s Done ; if not, we're done
move.w scVRefNum(a0), d1 ; is this entry in use?
beq.s NextEntry ; if not, skip to next entry
cmp.w vRefNum(a6), d1 ; is it for the target volume?
bne.s NextEntry ; if not, just skip it
move.l a0, entry(a6) ; otherwise, we've found a match
bra.s Done
NextEntry:
addq.l #SpecialCacheEntrySize, a0 ; advance the pointer to the next entry
bra.s CacheLoop ; ... and keep on checking
Done
restoreUnlinkReturn
endwith
endproc
;----------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------
;
; PROCEDURE AddSpecialCacheEntry(vRefNum: Integer; mapRefNum: Integer; trashDirID: LongInt);
;
; Add an entry in the special folder cache for a [trash] folder on a particular disk.
AddSpecialCacheEntry proc export
if FOR_FOLDERMGR_INIT then
import FolderMgrGlobalPtr
endif
parametersStackFrame
vRefNum ds.w 1 ; VRefNum for target volume
mapRefNum ds.w 1 ; File refnum of Trash map file
trashDirID ds.l 1 ; DirID of trash folder
endStackFrame
cacheHndl equ a1 ; Used to save a copy of the cache handle
cacheSize equ d1 ; Size of cache block at start
linkSave a1/d2
; Get the special cache handle from low-memory. Search it for an empty entry.
with FolderMgrGlobalRec, SpecialCacheEntry
if FOR_FOLDERMGR_INIT then
move.l FolderMgrGlobalPtr,d0
else
move.l ExpandMem,a0
move.l ExpandMemRec.emFolderCache(a0),d0 ; get cache handle
endif
bz.s Done ; no globals, get out
movea.l d0, a0 ; set up pointer to our globals
move.l specialCacheHndl(a0), d0 ; pick up special cache handle
bz.s Done ; no handle, get out
move.l d0,a0 ; get handle in address register, too
move.l a0, cacheHndl ; make a copy for later use
;;; tst.l (a0) ; check if it is purged
;;; bz.s Done
_GetHandleSize ; get the full length of the handle
move.l d0, cacheSize ; save original size in case block must be expanded
move.l (a0),a0 ; dereference the handle
CacheLoop:
subi.l #SpecialCacheEntrySize, d0 ; another entry left unseen?
blt.s GrowCache ; if not, time to grow the cache
tst.w scVRefNum(a0) ; is this entry in use?
beq.s FillEntry ; if so, skip to next entry
addq.l #SpecialCacheEntrySize, a0 ; advance the pointer to the next entry
bra.s CacheLoop ; ... and keep on checking
GrowCache:
movea.l cacheHndl, a0 ; recover orignal cache handle
move.l cacheSize, d0 ; ... and original size
addq.l #SpecialCacheEntrySize, d0 ; increase size by 1 entry
_SetHandleSize ; Try to grow the handle
bne.s Done ; if we get an error, punt
move.l (a0), a0 ; dereference handle again
add.l cacheSize, a0 ; point to new entry in handle
FillEntry:
move.w vRefNum(a6), scVRefNum(a0) ; otherwise, occupy this empty slot
move.w mapRefNum(a6), scTrashMapRefNum(a0) ; copy trash map refnum
move.l trashDirID(a6), scTrashDirID(a0); ... and the trash folder DirID
Done
restoreUnlinkReturn
endwith
endproc
;----------------------------------------------------------------------------------------------------
; FUNCTION ReleaseFolder(vRefNum: INTEGER; folderType: OSType): OSErr;
;
; This routine releases the locked byte for a given Trash folder and closes the Trash Map
; file. Passed vRefNum is assumed to be cooked.
;
ReleaseFolder proc export
resultsStackFrame
error ds.w 1 ; the resulting error code
parametersStackFrame
vRefNum ds.w 1 ; the vRefNum of the disk we want to look at
which ds.l 1 ; which folder we are interested in
localsStackFrame
PBRec ds.b ioHFQElSiz ; the folder parameter block
endStackFrame
linkSave a1
moveq #0,d0 ; assume returning success
cmp.l #kTrashFolderType,which(a6) ; is it the trash folder?
bne.s @done ; operation valid only for net trash files
with SpecialCacheEntry
rsrv.l ; make room for ptr to cache entry
push.w vRefNum(a6) ; pass vRefNum
jsr FindSpecialCacheEntry ; search the special cache
pop.l d0 ; check search result
beq.s @done ; if search fails, nothing to release
movea.l d0, a1 ; point to cache entry for this volume
clr.w scVRefNum(a1) ; mark this entry invalid
lea PBRec(a6), a0 ; Point to parameter block
moveq #(ioHFQElSiz/2)-1,d1 ; Compute DBRA loop index
@ClearPB
clr.w (a0)+ ; Clear a word in the PBRec
dbra d1,@ClearPB ;
lea PBRec(a6), a0 ; Point to parameter block
move.w scTrashMapRefNum(a1), ioRefNum(a0); Set up trash map file refnum
_Close ; close Trash Map file
endwith
@done
move.w d0,error(a6) ; no, use the CloseResFile error
restoreUnlinkReturn
endproc
;----------------------------------------------------------------------------------------------------
;
; PROCEDURE CleanOutFolder(vRefNum: Integer; dirForDelete: LongInt);
;
; Delete everything in a given folder, recursively if necessary
CleanOutFolder proc export
parametersStackFrame
vRefNum ds.w 1 ; VRefNum for target volume
dirForDelete ds.l 1 ; directory whose contents need to be deleted
localsStackFrame
PBRec ds.b ioHFQElSiz ; the folder parameter block
fileName ds.b 32 ; file name
endStackFrame
linkSave a1
lea PBRec(a6), a0 ; Point to parameter block
moveq #(ioHFQElSiz/2)-1,d0 ; Compute DBRA loop index
@ClearPB
clr.w (a0)+ ; Clear a word in the PBRec
dbra d0,@ClearPB ;
lea PBRec(a6), a0 ; Point to parameter block
lea fileName(a6),a1 ; load file name
move.l a1,ioNamePtr(a0) ; get file name from _GetCatInfo
@EnumLoop
move.w vRefNum(a6), ioVRefNum(a0) ; Set up target VRefNum
move.l dirForDelete(a6), ioDirID(a0) ; Set up target DirID
move.w #1,ioFDirIndex(a0) ; Set up enumeration index
_GetCatInfo ;
bne.s @Done ; Punt on error
btst #ioDirFlg, ioFlAttrib(a0) ; Is it a folder?
beq.s @DeleteIt ; Nope - just go for it
push.w vRefNum(a6) ; Copy target VRefNum for recursive call
push.l ioDirID(a0) ; Target DirID is newfound folder's ID
jsr CleanOutFolder ; Delete contents of folder just found
bne.s @Exit ; Punt on errors
lea PBRec(a6), a0 ; Point to parameter block
@DeleteIt
move.l dirForDelete(a6), ioDirID(a0) ; Reset to parent DirID
_HDelete ; Delete the file
beq.s @EnumLoop ; onwards to next entry
cmp.w #fLckdErr, d0 ; If locked, unlock it and then try delete
bne.s @Exit ; Punt immediately on any other error
_HRstFLock ; Try to reset the lock
_HDelete ; Try deleting the file again after unlocking it
bne.s @Exit ; Punt immediately on any error
bra.s @EnumLoop ; Otherwise try again on next entry
@Done
cmp.w #fnfErr, d0 ; Expected termination error?
bne.s @Exit ; If not, get out as-is
moveq #0, d0 ; Yes - all is well
@Exit
restoreUnlinkReturn
endwith
endproc
;----------------------------------------------------------------------------------------------------
SharedTrashCode proc export
;___________________________________________________________________________________
;
; GetNetTrashID - Get the DirID of the Network Trash folder for a given VRefNum
;
; Input:
; D0.W: VRefNum of target volume
;
; Output:
; D0.W: Error code
; D1.L: NetTrash DirID (if D0.W=0)
;___________________________________________________________________________________
GNIDRegs REG A0-A1 ; scratch registers used
GNIDStackFrame RECORD -(ioHFQElSiz) ; offset for local variables
FirstLocal EQU * ; First local variable
PBRec DS.B ioHFQElSiz ; Local parameter block for use
LocalSize EQU *-FirstLocal ; Size of local variable size
A6Link DS.L 1 ; A6 stack frame link (this should be zero!)
RetPC DS.L 1 ; Return address
LastArg EQU *
ArgsSize EQU *-LastArg ; Size of incoming arguments
;retValue DS.W 1 ; Space reserved for return value
ENDR
;
EXPORT GetNetTrashID
GetNetTrashID:
WITH GNIDStackFrame
LINK A6,#-LocalSize ; Allocate an HFS parameter block & other locals
MOVEM.L GNIDRegs, -(SP) ; Free for use as scratch
LEA PBRec(A6), A0 ; Point to parameter block
MOVEQ #(ioHFQElSiz/2)-1,D1 ; Compute DBRA loop index
@ClearPB CLR.W (A0)+ ; Clear a word in the PBRec
DBRA D1,@ClearPB ;
LEA PBRec(A6), A0 ; Point to parameter block
MOVE.W D0,ioVRefNum(A0) ; Set up for target volume
MOVE.L #fsRtDirID, ioDirID(A0) ; Folder is located in root directory
LEA NetTrashFolderName, A1 ; Point to the name of the Net Trash folder
MOVE.L A1,ioFileName(A0) ; Set up as target name
_GetCatInfo ; See if the folder exists
BNE.S @NoTrashFolder ; Try to recover from the shock
BTST #ioDirFlg, ioFlAttrib(A0) ; Make sure it's a folder
BNE.S @GotTrashFolder ; If it does, we're set
MOVE.W #dupFNErr, D0 ; Otherwise, return an error
BRA.S @Exit ; ... and get out
@NoTrashFolder:
CMP.W #fnfErr, D0 ; File not found error?
BNE.S @Exit ; If some other error, punt
MOVE.L #fsRtDirID, ioDirID(A0) ; Folder is located in root directory
_DirCreate ; Try to create the folder
BNE.S @Exit ; Punt on errors
MOVE.L ioDirID(A0), D1 ; Pick up the Network trash folder ID
MOVE.L #fsRtDirID, ioDirID(A0) ; Folder is located in root directory
_GetCatInfo ; Look up the Finder info for this folder
BNE.S @Exit ;
ORI.W #mFNameLock,ioDrUsrWds+frFlags(A0) ; make it rename-inhibit
BSET.B #bFndrInvisible,ioDrUsrWds+frFlags(A0) ; Invisible to Finder
MOVE.L #fsRtDirID, ioDirID(A0) ; Folder is located in root directory
_SetCatInfo ; Set new folder attributes
BNE.S @Exit ;
MOVE.L #administratorUser, ioACOwnerID(A0) ; Give the Network Trash Folder to the Administrator
MOVE.L #noGroup, ioACGroupID(A0) ; No group affiliation
MOVE.L #fullPrivileges, ioACAccess(A0) ; Free access to all other users inside
_SetDirAccess ; Set the access privileges for the folder
BRA.S @Exit ; We're done, D1=Network Trash folder DirID
@GotTrashFolder:
MOVE.L ioDirID(A0), D1 ; Pick up the folder ID
MOVEQ #0,D0 ; Indicate successful completion
@Exit MOVEM.L (SP)+, GNIDRegs ; Restore scratch registers
UNLK A6 ; Pop off stack frame
TST.W D0 ; Set condition codes
RTS ;
ENDWITH
;___________________________________________________________________________________
;
; GetTrashMapRefnum - Get the file refnum for the Trash map file for a given volume,
; given its Network Trash folder ID.
;
; Input:
; D0.W: VRefNum of target volume
; D1.L: Network Trash folder DirID
;
; Output:
; D0.W: Error code
; D1.W: Trash map file refnum (if D0.W=0)
;___________________________________________________________________________________
GMRRegs REG A0-A1 ; scratch registers used
GMRStackFrame RECORD -(ioHFQElSiz+2+4) ; offset for local variables
FirstLocal EQU * ; First local variable
PBRec DS.B ioHFQElSiz ; Local parameter block for use
TargetVRef DS.W 1 ; Target volume VRefNum
NetTrashID DS.L 1 ; Net Trash DirID
LocalSize EQU *-FirstLocal ; Size of local variable size
A6Link DS.L 1 ; A6 stack frame link (this should be zero!)
RetPC DS.L 1 ; Return address
LastArg EQU *
ArgsSize EQU *-LastArg ; Size of incoming arguments
;retValue DS.W 1 ; Space reserved for return value
ENDR
EXPORT GetTrashMapRefnum
;
GetTrashMapRefnum:
WITH GMRStackFrame
LINK A6,#-LocalSize ; Allocate an HFS parameter block & other locals
MOVEM.L GMRRegs,-(SP) ; Free for use as scratch
MOVE.W D0, TargetVRef(A6) ; Save copy of target volume VRefNum
MOVE.L D1, NetTrashID(A6) ; Save copy of Network Trash folder ID
LEA PBRec(A6), A0 ; Point to parameter block
MOVEQ #(ioHFQElSiz/2)-1,D0 ; Compute DBRA loop index
@ClearPB CLR.W (A0)+ ; Clear a word in the PBRec
DBRA D0,@ClearPB ;
@OpenMapFile;
LEA PBRec(A6), A0 ; Point to parameter block
MOVE.W TargetVRef(A6), ioVRefNum(A0) ; Set up to specify target volume
MOVE.L NetTrashID(A6), ioDirID(A0) ; Set up to look in Network Trash folder
LEA TrashMapName, A1 ; Point to the name of the trash map file
MOVE.L A1, ioFileName(A0) ; Set up for _Open
MOVE.B #fsRdPerm, ioPermssn(A0); Open the file Read-Only
_HOpen ; Try to open the file
BEQ.S @Done ; If it works, we're set
CMP.W #fnfErr, D0 ; File not found?
BNE.S @Exit ; Nope - this is just too much to handle
_HCreate ; Yes - try to create the file
BNE.S @Exit ; Give up at the first hint of trouble
;
; We've just created a new Trash map file -
; Set its Finder attributes to make it invisible and lock the name.
;
MOVE.L NetTrashID(A6), ioDirID(A0) ; Reset the DirID to the Network Trash Folder
_GetCatInfo ; Get the file's information
BNE.S @Exit ; This SHOULD never fail...
ORI.W #mFNameLock,ioFlUsrWds+fdFlags(A0) ; make it rename-inhibit
BSET #bFndrInvisible, ioFlUsrWds+fdFlags(A0) ; Set the invisible bit
MOVE.L NetTrashID(A6), ioDirID(A0) ; Reset the DirID to the Network Trash Folder
_SetCatInfo ; Set the new attributes for the file
BNE.S @Exit ; This SHOULD be even LESS like to fail...
BRA.S @OpenMapFile ; Try to open the file again
@Done MOVE.W ioRefNum(A0), D1 ; Return file refnum
MOVEQ #0, D0 ; Indicate total stunning success
@Exit MOVEM.L (SP)+, GMRRegs ; Restore scratch registers
UNLK A6 ; Pop off stack frame
TST.W D0 ; Set condition codes
RTS ;
ENDWITH
;___________________________________________________________________________________
;
; GetTrashCanID - Get the DirID of the trash folder for use on a given volume/Network Trash folder
;
; Input:
; D0.W: VRefNum of target volume
; D1.L: Network Trash folder DirID
; D2.W: RefNum of Trash Can Usage Map file
;
; Output:
; D0.W: Error code
; D1.L: NetTrash DirID (if D0.W=0)
;___________________________________________________________________________________
GetTrashRegs REG A0-A1/D2-D3 ; scratch registers used
GTIDStackFrame RECORD -(ioHFQElSiz+2+4+2+32+6+4) ; offset for local variables
FirstLocal EQU * ; First local variable
PBRec DS.B ioHFQElSiz ; Local parameter block for use
TargetVRef DS.W 1 ; Target volume VRefNum
NetTrashID DS.L 1 ; Net Trash DirID
TrashMapRefnum DS.W 1 ; File refnum of trash map file
FileName DS.B 32 ; 32 bytes for file name storage
TrashNumber DS.B 6 ; Str5 for trash can number string
TrashCanID DS.L 1 ; DirID of trash can
LocalSize EQU *-FirstLocal ; Size of local variable size
A6Link DS.L 1 ; A6 stack frame link (this should be zero!)
RetPC DS.L 1 ; Return address
LastArg EQU *
ArgsSize EQU *-LastArg ; Size of incoming arguments
;retValue DS.W 1 ; Space reserved for return value
ENDR
EXPORT GetTrashCanID
;
GetTrashCanID:
WITH GTIDStackFrame
LINK A6,#-LocalSize ; Allocate an HFS parameter block & other locals
MOVEM.L GetTrashRegs,-(SP) ; Free for use as scratch
MOVE.W D0, TargetVRef(A6) ; Save copy of target volume VRefNum
MOVE.L D1, NetTrashID(A6) ; Save copy of Network Trash folder ID
MOVE.W D2, TrashMapRefnum(A6) ; stash file refnum for future use
LEA PBRec(A6), A0 ; Point to parameter block
MOVEQ #(ioHFQElSiz/2)-1,D0 ; Compute DBRA loop index
@ClearPB CLR.W (A0)+ ; Clear a word in the PBRec
DBRA D0,@ClearPB ;
MOVEQ #1,D3 ; Starting guess at trashcan #
;
; Start by enumerating the Network Trash Folder on the detination volume,
; looking for existing trash folders that aren't currently in use:
;
LEA PBRec(A6), A0 ; Point to parameter block
MOVE.W TargetVRef(A6),ioVRefNum(A0) ; Set up for target volume
MOVE.L NetTrashID(A6),ioDirID(A0) ; Set up Network Trash folder ID
LEA FileName(A6), A1 ; Point to local file name buffer
MOVE.L A1,ioFileName(A0) ; Set up to be return on _GetCatInfo
MOVEQ #0,D2 ; Initialize enumerate index
@EnumLoop LEA PBRec(A6), A0 ; Point to parameter block
MOVE.L NetTrashID(A6), ioDirID(A0) ; Reset target DirID
ADDQ.W #1,D2 ; Bump up to request next object in folder
MOVE.W D2,ioFDirIndex(A0) ; Set up requested index
_GetCatInfo ; See if there's a folder by that index
BNE @NewTrashCan ; Stop enumerating on any error
BTST #ioDirFlg, ioFlAttrib(A0) ; Is it a folder?
BEQ.S @EnumLoop ; If not, skip it
MOVE.L ioDirID(A0), TrashCanID(A6) ; Save a copy of the folder's DirID
BTST #7,ioFlAttrib+1(A0) ; Check 'owner' bit on folder
BEQ.S @CheckTrashCan ; If we're the owner, go check it out
MOVE.B ioFlAttrib+1(A0), D0 ; Pick up the access privilege summary
ANDI.B #$07, D0 ; Mask off all but 3-bit summary
BNE.S @EnumLoop ; Some access wasn't allowed
;
; We've come across a folder we don't own, but DO have full privileges to:
; Even if the owner flag isn't set, we might be the owner of the machine, in which
; case the owner flag might not be set because the root of the volume is not shared
; with the network and we gained access by virtue of being the owner of the machine.
; Since the Network Trash folder will lie outside the shared area, the owner flag
; will never be set for any folder. Explicitly check the owner ID here, then:
;
; NOTE: Should probably do an explicity check of the ioACOwnerID agains the login
; user ID here, but this'll do in a pinch:
;
MOVE.L NetTrashID(A6), ioDirID(A0) ; Reset target DirID
CLR.W ioFDirIndex(A0) ;
_GetDirAccess ; Look up the access privileges
BNE.S @EnumLoop ; Punt on any error
CMPI.L #administratorUser, ioACOwnerID(A0) ; Owned by Administrator?
BNE.S @EnumLoop ; Nope - who knows what's going on
MOVE.L ioACAccess(A0), D0 ; Pick up folder's access privileges
ANDI.L #$00FFFFFF, D0 ; Strip off access summary
CMPI.L #ownerPrivileges, D0 ; Set to owner only access?
BNE.S @EnumLoop ; If not, something strange is going on
;
; Check to see if the folder just found is 'Trash Can #xxx':
;
@CheckTrashCan:
LEA FileName(A6), A0 ; Point to local file name buffer
LEA NetTrashPrefix, A1 ; Point to trash folder prefix
MOVE.B (A1),D0 ; Pick up length of prefix
CMP.B (A0), D0 ; Check if folder length is > prefix length
BGE.S @EnumLoop ; If so, there's no interest
SWAP D0 ; Switch string length into top byte
MOVE.B (A1)+, D0 ; Pick up prefix length & advance name ptr
ADDQ.L #1,A0 ; Ignore actual length of file name
_CmpString ; See if the string is identical
BNE.S @EnumLoop ; If not, ignore it
;
; Get the number of the trash can from the name:
;
LEA FileName(A6), A0 ; Point to the file name
MOVE.B (A0)+,D0 ; Pick up the name length
LEA NetTrashPrefix, A1 ; Point to trash folder prefix
MOVEQ #0, D1 ; Clear top 3 bytes
MOVE.B (A1), D1 ; Pick up prefix length
ADDA.L D1, A0 ; Skip prefix in file name
SUB.B D1, D0 ; Subtract from file name length to yield number length
CLR.L D3 ; Initialize the number
@GetNumLoop SUBQ.B #1,D0 ; One less digit to process
BLT.S @GotTrashNum ; If no chars left, D3 holds the trash can #
MOVEQ #0, D1 ; Clear top 3 bytes in longword
MOVE.B (A0)+, D1 ; Pick up a character from the name
CMPI.B #'0', D1 ; Is < 0?
BLT @EnumLoop ; If so, it's a bad name
CMPI.B #'9', D1 ; Is it > 9?
BGT @EnumLoop ; If so, it's a bad name
SUBI.B #'0',D1 ; Convert to decimal digit
MULU.W #10, D3 ; Multiply number so far by 10,
ADD.L D1,D3 ; ... add in the new digit
BRA.S @GetNumLoop ; ... and loop to get the next digit
@GotTrashNum:
MOVE.L D3, -(SP) ; Specify trash can of interest
BSR SeizeTrashCan ; Try to get the semaphore byte locked
BNE @EnumLoop ; If can't lock it, try another folder
;
; We've found an existing trash can suitable for our use - start
; by emptying out whatever files or folder may have been left in there:
;
MOVE.W TargetVRef(A6), -(SP) ; Point to target volume VRefNum
MOVE.L TrashCanID(A6), -(SP) ; Recover trash can folder ID
JSR CleanOutFolder ; Clean out the indicated folder
BNE @EnumLoop ; Punt on error
BRA @Done ; Otherwise, we're set
;
; No suitable existing trash can was found - time to create a brand new one.
; D3 currently holds the number of the highest trash can in the Network Trash
; folder: adding 1 yields a pretty good bet for the next available trash folder:
;
@NewTrashCan:
ADDQ.L #1, D3 ; Bump up to the first unused number
MOVE.L D3, -(SP) ; Pass the next highest trash can #
BSR SeizeTrashCan ; Try to get the semaphore byte locked
BEQ.S @GotTrashCan ; Success, we got the Trash can
CMPI.W #paramErr, D0 ; Byte range locking not implemented?
BEQ @Exit ; Yes, return the error. Don't loop for next one.
CMPI.W #ioErr, D0 ; Byte range locking not implemented? (special case for Caps)
BEQ @Exit ; Yes, return the error. Don't loop for next one.
CMPI.W #extFSErr, D0 ; External file system error?
BEQ @Exit ; Yes, return the error. Don't loop for next one.
BRA.S @NewTrashCan ; ... and try again for any other error
@GotTrashCan:
LEA NetTrashPrefix, A0 ; Point to the standard Trash can name prefix
MOVEQ #0,D0 ; Clean upper 3 bytes
MOVE.B (A0), D0 ; Pick up string length
ADDQ.L #1, D0 ; Add 1 for the length byte itself
MOVE.L D0, D1 ; Make a copy for use in a moment
LEA FileName(A6), A1 ; Point to the target file name
_BlockMove ; Copy the name prefix
ADDA.L D1, A1 ; Point to the end of the file name
LEA TrashNumber+6(A6), A0 ; Point to last digit of trash can #
MOVE.L D3, D2 ; Make a copy to convert
MOVEQ #0, D1 ; Clear digit count
@NextDigit ADDQ.L #1, D1 ; One more digit generated
DIVU #10, D2 ; Divide by D3 and use the remainder
SWAP D2 ; Get the remainder in the low word
ADD.B #'0', D2 ; Change to decimal digit
MOVE.B D2, -(A0) ; Add digit to number
CLR.W D2 ; Clear out the processed digit
SWAP D2 ; Get quotient in D3.L again
BNE.S @NextDigit ; If the result is non-zero, go get another one
MOVE.L D1, D0 ; Get count of # of digits generated
_BlockMove ; Tack on the digit sequence to the file name
LEA FileName(A6), A1 ; Point to start of whole file name
ADD.B D1, (A1) ; Add digit count to overall length
;
; Create the trash folder by the newly generated trash can name:
;
LEA PBRec(A6), A0 ; Point to the parameter block again
MOVE.L A1, ioFileName(A0) ; Set up the newly chosen name
MOVE.L NetTrashID(A6), ioDirID(A0) ; Create folder in Network Trash folder
_DirCreate ; Create the new trash can
BEQ.S @SetupFldr ; Continue if all went well
CMPI.W #dupFNErr, D0 ; Folder by that name exists already?
BNE.S @Exit ; Nope - something else went wrong
;
; The trash can just selected was not in use but DID already exist.
; Since it wasn't selected for recycling during the Network Trash Folder enumeration,
; it must not have been accessible. Release the trash can flag and try again.
;
MOVE.L D3, -(SP) ; Pass trash can # just allocated
BSR ReleaseTrashCan ; Release the trash can semaphore byte
BRA.S @NewTrashCan ; ... and try again
@SetupFldr MOVE.L ioDirID(A0), TrashCanID(A6) ; Pick up the Network trash folder ID
;
; Make sure only the user has access privileges:
;
MOVE.L NetTrashID(A6), ioDirID(A0) ; Folder is located in Network Trash folder
_GetDirAccess ; Look up the access privileges assigned to the folder
CMPI.L #ownerPrivileges, ioACAccess(A0) ; Already set to owner only access?
BEQ.S @SetFldrAttrib ; Yes - skip to set the folder attributes
MOVE.L #noGroup, ioACGroupID(A0) ; No group affiliation
MOVE.L #ownerPrivileges, ioACAccess(A0) ; Access only to owner
_SetDirAccess ; Set the access privileges for the folder
;
; Make the folder invisible and lock the name:
;
@SetFldrAttrib:
MOVE.L NetTrashID(A6), ioDirID(A0) ; Folder is located in Network Trash folder
_GetCatInfo ; Look up the Finder info for this folder
BNE.S @Exit ; Must be able to find it now
ORI.W #mFNameLock,ioDrUsrWds+frFlags(A0) ; make it rename-inhibit
BSET.B #bFndrInvisible,ioDrUsrWds+frFlags(A0) ; Invisible to Finder
MOVE.L NetTrashID(A6), ioDirID(A0) ; Folder is located in Network Trash folder
_SetCatInfo ; Set new folder attributes
BNE.S @Exit ; What could POSSIBLY go wrong, eh?
@Done MOVE.L TrashCanID(A6), D1 ; Set up return value
MOVEQ #0, D0 ; Total stunning success
@Exit MOVEM.L (SP)+, GetTrashRegs ; Restore scratch registers
UNLK A6 ; Pop off stack frame
TST.W D0 ; Set condition codes
RTS ;
DC.B ($80+'G') ; Just for the debugger
DC.B 'TRASHCA' ;
DC.W 0
;
; Called with:
; A6 - GetTrashCanID frame pointer
; 4(SP) - Trash folder ID to seize
;
; Sets:
; D0 - result code
;
SeizeTrashCan:
MOVE.L A0, -(SP) ; Free for use as scratch
LEA PBRec(A6), A0 ; Point to parameter block
MOVE.W TrashMapRefnum(A6), ioRefNum(A0) ; Set up trash map file refnum
MOVE.W #fsFromStart, ioPosMode(A0) ; Indicate offset is from start of file
MOVE.L 8(SP), ioPosOffset(A0) ; Set up target byte
MOVE.L #1, ioReqCount(A0) ; Lock range is always just 1 byte
_LockRng ; Try to lock the byte in question
MOVEA.L (SP)+, A0 ; Restore scratch register
MOVE.L (SP), 4(SP) ; Clobber the incoming argument w. return address
TST.L (SP)+ ; Pop off the [copied] return address
TST.W D0 ; Set condition codes
RTS ;
;
; Called with:
; A6 - GetTrashCanID frame pointer
; 4(SP) - Trash folder ID to release
;
; Sets:
; D0 - result code
;
ReleaseTrashCan:
MOVE.L A0, -(SP) ; Free for use as scratch
LEA PBRec(A6), A0 ; Point to parameter block
MOVE.W TrashMapRefnum(A6), ioRefNum(A0) ; Set up trash map file refnum
MOVE.W #fsFromStart, ioPosMode(A0) ; Indicate offset is from start of file
MOVE.L 8(SP), ioPosOffset(A0) ; Set up target byte
MOVE.L #1, ioReqCount(A0) ; Lock range is always just 1 byte
_UnlockRng ; Unlock the byte in question
MOVEA.L (SP)+, A0 ; Restore scratch register
MOVE.L (SP), 4(SP) ; Clobber the incoming argument w. return address
TST.L (SP)+ ; Pop off the [copied] return address
TST.W D0 ; Set condition codes
RTS ;
ENDWITH
endproc
;----------------------------------------------------------------------------------------------------
end