; ; 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): ; ; 10/29/92 SWC Backed out and fixed the INCLUDE filenames. ; 10/28/92 SWC Changed the INCLUDEs to a LOAD of StandardEqu.d. ; 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 (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 ; 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