From: <ama...@us...> - 2006-04-30 00:47:34
|
Revision: 6360 Author: amaxwell Date: 2006-04-29 17:47:25 -0700 (Sat, 29 Apr 2006) ViewCVS: http://svn.sourceforge.net/bibdesk/?rev=6360&view=rev Log Message: ----------- Add FSCopyObject from Apple. This is thread safe, and preserves Finder info, whereas NSFileManager fails on both counts. Modified Paths: -------------- trunk/bibdesk/Bibdesk.xcodeproj/project.pbxproj Added Paths: ----------- trunk/bibdesk/FSCopyObject.c trunk/bibdesk/FSCopyObject.h trunk/bibdesk/GenLinkedList.c trunk/bibdesk/GenLinkedList.h Modified: trunk/bibdesk/Bibdesk.xcodeproj/project.pbxproj =================================================================== --- trunk/bibdesk/Bibdesk.xcodeproj/project.pbxproj 2006-04-29 21:39:03 UTC (rev 6359) +++ trunk/bibdesk/Bibdesk.xcodeproj/project.pbxproj 2006-04-30 00:47:25 UTC (rev 6360) @@ -653,6 +653,10 @@ F9E6C9BA099A99ED00A8B97D /* NSFileManager_ExtendedAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = F9E6C936099A8FEF00A8B97D /* NSFileManager_ExtendedAttributes.m */; }; F9E7591B0942AE7C00CFB46F /* BDSKDateStringFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = F9E759190942AE7C00CFB46F /* BDSKDateStringFormatter.h */; }; F9E7591C0942AE7C00CFB46F /* BDSKDateStringFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = F9E7591A0942AE7C00CFB46F /* BDSKDateStringFormatter.m */; }; + F9ED4DAC0A0437E20048D912 /* FSCopyObject.c in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4DA80A0437E20048D912 /* FSCopyObject.c */; }; + F9ED4DAD0A0437E20048D912 /* FSCopyObject.h in Headers */ = {isa = PBXBuildFile; fileRef = F9ED4DA90A0437E20048D912 /* FSCopyObject.h */; }; + F9ED4DAE0A0437E20048D912 /* GenLinkedList.c in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4DAA0A0437E20048D912 /* GenLinkedList.c */; }; + F9ED4DAF0A0437E20048D912 /* GenLinkedList.h in Headers */ = {isa = PBXBuildFile; fileRef = F9ED4DAB0A0437E20048D912 /* GenLinkedList.h */; }; F9EF21B608B1572500AAC9A9 /* BDSKScrollableTextField.h in Headers */ = {isa = PBXBuildFile; fileRef = F9EF21B208B1572500AAC9A9 /* BDSKScrollableTextField.h */; }; F9EF21B708B1572600AAC9A9 /* BDSKScrollableTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = F9EF21B308B1572500AAC9A9 /* BDSKScrollableTextField.m */; }; F9EF21B808B1572600AAC9A9 /* BDSKScrollableTextFieldCell.h in Headers */ = {isa = PBXBuildFile; fileRef = F9EF21B408B1572500AAC9A9 /* BDSKScrollableTextFieldCell.h */; }; @@ -1083,7 +1087,7 @@ 3DFF35600816B55B0052F37C /* forward_small.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = forward_small.tiff; path = Images/forward_small.tiff; sourceTree = "<group>"; }; 3DFF35620816B5780052F37C /* reload_small.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = reload_small.tiff; path = Images/reload_small.tiff; sourceTree = "<group>"; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; }; - 8D1107320486CEB800E47090 /* BibDesk.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BibDesk.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D1107320486CEB800E47090 /* BibDesk.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = BibDesk.app; sourceTree = BUILT_PRODUCTS_DIR; }; CE1A7ED4090BDEB600ACC864 /* BDSKHeaderPopUpButtonCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BDSKHeaderPopUpButtonCell.h; sourceTree = "<group>"; }; CE1A7ED5090BDEB600ACC864 /* BDSKHeaderPopUpButtonCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BDSKHeaderPopUpButtonCell.m; sourceTree = "<group>"; }; CE1A7EDD090BDF1C00ACC864 /* BDSKHeaderPopUpButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BDSKHeaderPopUpButton.h; sourceTree = "<group>"; }; @@ -1478,6 +1482,10 @@ F9E6C936099A8FEF00A8B97D /* NSFileManager_ExtendedAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSFileManager_ExtendedAttributes.m; sourceTree = "<group>"; }; F9E759190942AE7C00CFB46F /* BDSKDateStringFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BDSKDateStringFormatter.h; sourceTree = "<group>"; }; F9E7591A0942AE7C00CFB46F /* BDSKDateStringFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BDSKDateStringFormatter.m; sourceTree = "<group>"; }; + F9ED4DA80A0437E20048D912 /* FSCopyObject.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = FSCopyObject.c; sourceTree = "<group>"; }; + F9ED4DA90A0437E20048D912 /* FSCopyObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSCopyObject.h; sourceTree = "<group>"; }; + F9ED4DAA0A0437E20048D912 /* GenLinkedList.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = GenLinkedList.c; sourceTree = "<group>"; }; + F9ED4DAB0A0437E20048D912 /* GenLinkedList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GenLinkedList.h; sourceTree = "<group>"; }; F9EF21B208B1572500AAC9A9 /* BDSKScrollableTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BDSKScrollableTextField.h; sourceTree = "<group>"; }; F9EF21B308B1572500AAC9A9 /* BDSKScrollableTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BDSKScrollableTextField.m; sourceTree = "<group>"; }; F9EF21B408B1572500AAC9A9 /* BDSKScrollableTextFieldCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BDSKScrollableTextFieldCell.h; sourceTree = "<group>"; }; @@ -1660,6 +1668,10 @@ 29B97315FDCFA39411CA2CEA /* Other Sources */ = { isa = PBXGroup; children = ( + F9ED4DA80A0437E20048D912 /* FSCopyObject.c */, + F9ED4DA90A0437E20048D912 /* FSCopyObject.h */, + F9ED4DAA0A0437E20048D912 /* GenLinkedList.c */, + F9ED4DAB0A0437E20048D912 /* GenLinkedList.h */, F9484D8908D68C3200153BDA /* KFAppleScriptHandlerAdditionsCore.h */, F9484D8A08D68C3200153BDA /* KFAppleScriptHandlerAdditionsCore.m */, F9484D8B08D68C3200153BDA /* KFASHandlerAdditions-TypeTranslation.h */, @@ -2518,6 +2530,8 @@ CE573D2609F6A0300020EC16 /* NSIndexSet_BDSKExtensions.h in Headers */, F946DCE809FDC4B600D471DF /* BDSKAsynchronousDOServer.h in Headers */, CE34E9790A03C16B00222942 /* BDSKConditionsView.h in Headers */, + F9ED4DAD0A0437E20048D912 /* FSCopyObject.h in Headers */, + F9ED4DAF0A0437E20048D912 /* GenLinkedList.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3458,6 +3472,8 @@ CE573D2709F6A0300020EC16 /* NSIndexSet_BDSKExtensions.m in Sources */, F946DCE909FDC4B600D471DF /* BDSKAsynchronousDOServer.m in Sources */, CE34E97A0A03C16B00222942 /* BDSKConditionsView.m in Sources */, + F9ED4DAC0A0437E20048D912 /* FSCopyObject.c in Sources */, + F9ED4DAE0A0437E20048D912 /* GenLinkedList.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; Added: trunk/bibdesk/FSCopyObject.c =================================================================== --- trunk/bibdesk/FSCopyObject.c (rev 0) +++ trunk/bibdesk/FSCopyObject.c 2006-04-30 00:47:25 UTC (rev 6360) @@ -0,0 +1 @@ +/* File: FSCopyObject.c Contains: A Copy/Delete Files/Folders engine which uses HFS+ API's. This code takes some tricks/techniques from MoreFilesX and MPFileCopy, wraps them all up into an easy to use API, and adds a bunch of features. It will run on Mac OS 9.1 through 9.2.x and 10.1.x and up (Classic, Carbon and Mach-O) Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this Apple software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this Apple software. In consideration of your agreement to abide by the following terms, and subject to these terms, Apple grants you a personal, non-exclusive license, under Apple\xD5s copyrights in this original Apple software (the "Apple Software"), to use, reproduce, modify and redistribute the Apple Software, with or without modifications, in source and/or binary forms; provided that if you redistribute the Apple Software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the Apple Software. Neither the name, trademarks, service marks or logos of Apple Computer, Inc. may be used to endorse or promote products derived from the Apple Software without specific prior written permission from Apple. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Apple herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated. The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Copyright \xA9 2002-2004 Apple Computer, Inc., All Rights Reserved */ #include "FSCopyObject.h" #include "GenLinkedList.h" #if !TARGET_API_MAC_OSX #include <UnicodeConverter.h> #endif #include <stddef.h> #include <string.h> #pragma mark ----- Tunable Parameters ----- /* The following constants control the behavior of the copy engine. */ enum { /* BufferSizeForVolSpeed */ /* kDefaultCopyBufferSize = 2L * 1024 * 1024,*/ /* 2MB, Fast but not very responsive. */ kDefaultCopyBufferSize = 256L * 1024, /* 256kB, Slower but can still use machine. */ kMaximumCopyBufferSize = 2L * 1024 * 1024, kMinimumCopyBufferSize = 1024 }; enum { /* CheckForDestInsideSrc */ errFSDestInsideSource = -1234 }; enum { /* for use with PBHGetDirAccess in IsDropBox */ kPrivilegesMask = kioACAccessUserWriteMask | kioACAccessUserReadMask | kioACAccessUserSearchMask, /* for use with FSGetCatalogInfo and FSPermissionInfo->mode */ /* from sys/stat.h... note -- sys/stat.h definitions are in octal */ /* */ /* You can use these values to adjust the users/groups permissions */ /* on a file/folder with FSSetCatalogInfo and extracting the */ /* kFSCatInfoPermissions field. See code below for examples */ kRWXUserAccessMask = 0x01C0, kReadAccessUser = 0x0100, kWriteAccessUser = 0x0080, kExecuteAccessUser = 0x0040, kRWXGroupAccessMask = 0x0038, kReadAccessGroup = 0x0020, kWriteAccessGroup = 0x0010, kExecuteAccessGroup = 0x0008, kRWXOtherAccessMask = 0x0007, kReadAccessOther = 0x0004, kWriteAccessOther = 0x0002, kExecuteAccessOther = 0x0001, kDropFolderValue = kWriteAccessOther | kExecuteAccessOther }; #define kNumObjects 80 #define VolHasCopyFile(volParms) (((volParms)->vMAttrib & (1L << bHasCopyFile)) != 0) #pragma mark ----- Struct Definitions ----- /* The CopyParams data structure holds the copy buffer used */ /* when copying the forks over, as well as special case */ /* info on the destination */ struct CopyParams { void *copyBuffer; ByteCount copyBufferSize; Boolean copyingToDropFolder; Boolean copyingToLocalVolume; Boolean volHasCopyFile; DupeAction dupeAction; }; typedef struct CopyParams CopyParams; /* The FilterParams data structure holds the date and info */ /* that the caller wants passed into the Filter Proc, as well */ /* as the Filter Proc Pointer itself */ struct FilterParams { FSCatalogInfoBitmap whichInfo; CopyObjectFilterProcPtr filterProcPtr; Boolean containerChanged; Boolean wantSpec; Boolean wantName; void *yourDataPtr; }; typedef struct FilterParams FilterParams; /* The ForkTracker data structure holds information about a specific fork, */ /* specifically the name and the refnum. We use this to build a list of */ /* all the forks before we start copying them. We need to do this because */ /* if we're copying into a drop folder, we must open all the forks before */ /* we start copying data into any of them. */ /* Plus it's a convenient way to keep track of all the forks... */ struct ForkTracker { HFSUniStr255 forkName; SInt64 forkSize; SInt16 forkDestRefNum; }; typedef struct ForkTracker ForkTracker; typedef ForkTracker *ForkTrackerPtr; /* The FolderListData data structure holds FSRefs to the source and */ /* coorisponding destination folder, as well as which level its on */ /* for use in ProcessFolderList. */ struct FolderListData { FSRef sourceDirRef; FSRef destDirRef; UInt32 level; }; typedef struct FolderListData FolderListData; /* The FSCopyFolderGlobals data structure holds the information needed to */ /* copy a directory */ struct FSCopyFolderGlobals { FSRef *sourceDirRef; FSRef *destDirRef; FSCatalogInfo *catInfoList; FSRef *srcRefList; HFSUniStr255 *nameList; GenLinkedList folderList; GenIteratorPtr folderListIter; CopyParams *copyParams; FilterParams *filterParams; Boolean containerChanged; ItemCount maxLevels; ItemCount currentLevel; }; typedef struct FSCopyFolderGlobals FSCopyFolderGlobals; /* The FSDeleteObjectGlobals data structure holds information needed to */ /* recursively delete a directory */ struct FSDeleteObjectGlobals { FSCatalogInfo catalogInfo; /* FSCatalogInfo */ ItemCount actualObjects; /* number of objects returned */ OSErr result; /* result */ }; typedef struct FSDeleteObjectGlobals FSDeleteObjectGlobals; #pragma mark ----- Local Prototypes ----- static OSErr FSCopyObjectPreflight ( const FSRef *source, const FSRef *destDir, const DupeAction dupeAction, FSCatalogInfo *sourceCatInfo, CopyParams *copyParams, /* can be NULL */ HFSUniStr255 *newObjectName, FSRef *deleteMeRef, Boolean *isReplacing, Boolean *isDirectory ); static OSErr FSCopyFile ( const FSRef *source, const FSRef *destDir, const FSCatalogInfo *sourceCatInfo, const HFSUniStr255 *newFileName, CopyParams *copyParams, FilterParams *filterParams, FSRef *newFileRef, /* can be NULL */ FSSpec *newFileSpec ); /* can be NULL */ static OSErr CopyFile ( const FSRef *source, FSCatalogInfo *sourceCatInfo, const FSRef *destDir, const HFSUniStr255 *destName, /* can be NULL */ CopyParams *copyParams, FSRef *newRef, /* can be NULL */ FSSpec *newSpec ); /* can be NULL */ static OSErr FSUsePBHCopyFile ( const FSRef *srcFileRef, const FSRef *dstDirectoryRef, const HFSUniStr255 *destName, /* can be NULL (no rename during copy) */ TextEncoding textEncodingHint, FSRef *newRef, /* can be NULL */ FSSpec *newSpec ); /* can be NULL */ static OSErr DoCopyFile ( const FSRef *source, FSCatalogInfo *sourceCatInfo, const FSRef *destDir, const HFSUniStr255 *destName, CopyParams *params, FSRef *newRef, /* can be NULL */ FSSpec *newSpec ); /* can be NULL */ static OSErr FSCopyFolder ( const FSRef *source, const FSRef *destDir, const FSCatalogInfo *sourceCatInfo, const HFSUniStr255 *newFoldName, CopyParams *copyParams, FilterParams *filterParams, ItemCount maxLevels, FSRef *outDirRef, /* can be NULL */ FSSpec *outDirSpec ); /* can be NULL */ static OSErr ProcessFolderList ( FSCopyFolderGlobals *folderGlobals ); static OSErr CopyFolder ( FSCopyFolderGlobals *folderGlobals ); static OSErr CheckForDestInsideSrc ( const FSRef *source, const FSRef *destDir ); static OSErr CopyForks ( const FSRef *source, const FSRef *dest, CopyParams *params ); static OSErr CopyForksToDisk ( const FSRef *source, const FSRef *dest, CopyParams *params ); static OSErr CopyForksToDropBox ( const FSRef *source, const FSRef *dest, CopyParams *params ); static OSErr OpenAllForks ( const FSRef *dest, GenLinkedList *forkList ); static OSErr WriteFork ( const SInt16 srcRefNum, const SInt16 destRefNum, const CopyParams *params, const SInt64 forkSize ); static UInt32 CalcBufferSizeForVol ( const GetVolParmsInfoBuffer *volParms, UInt32 volParmsSize ); static UInt32 BufferSizeForVolSpeed ( UInt32 volumeBytesPerSecond ); static OSErr FSDeleteFolder ( const FSRef *container ); static void FSDeleteFolderLevel ( const FSRef *container, FSDeleteObjectGlobals *theGlobals ); static OSErr IsDropBox ( const FSRef *source, Boolean *isDropBox ); static OSErr GetMagicBusyCreateDate( UTCDateTime *date ); static OSErr FSGetVRefNum ( const FSRef *ref, FSVolumeRefNum *vRefNum ); static OSErr FSGetVolParms ( FSVolumeRefNum volRefNum, UInt32 bufferSize, GetVolParmsInfoBuffer*volParmsInfo, UInt32 *actualInfoSize ); /* Can Be NULL */ static OSErr UniStrToPStr ( const HFSUniStr255 *uniStr, TextEncoding textEncodingHint, Boolean isVolumeName, Str255 pStr ); static OSErr FSMakeFSRef ( FSVolumeRefNum volRefNum, SInt32 dirID, ConstStr255Param name, FSRef *ref ); static OSErr SetupDestination ( const FSRef *destDir, const DupeAction dupeAction, HFSUniStr255 *sourceName, FSRef *deleteMeRef, Boolean *isReplacing); static OSErr GetUniqueName ( const FSRef *destDir, HFSUniStr255 *sourceName ); static OSErr GetObjectName ( const FSRef *sourceRef, HFSUniStr255 *sourceName, TextEncoding *sourceEncoding ); static OSErr CreateFolder ( const FSRef *sourceRef, const FSRef *destDirRef, const FSCatalogInfo *catalogInfo, const HFSUniStr255 *folderName, CopyParams *params, FSRef *newFSRefPtr, FSSpec *newFSSpecPtr ); static OSErr DoCreateFolder ( const FSRef *sourceRef, const FSRef *destDirRef, const FSCatalogInfo *catalogInfo, const HFSUniStr255 *folderName, CopyParams *params, FSRef *newFSRefPtr, FSSpec *newFSSpecPtr); static pascal void MyDisposeDataProc ( void *pData ); static pascal void MyCloseForkProc ( void *pData ); /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ #pragma mark ----- Copy Objects ----- /* This routine acts as the top level of the copy engine. */ OSErr FSCopyObject( const FSRef *source, const FSRef *destDir, ItemCount maxLevels, FSCatalogInfoBitmap whichInfo, DupeAction dupeAction, const HFSUniStr255 *newObjectName, /* can be NULL */ Boolean wantFSSpec, Boolean wantName, CopyObjectFilterProcPtr filterProcPtr, /* can be NULL */ void *yourDataPtr, /* can be NULL */ FSRef *newObjectRef, /* can be NULL */ FSSpec *newObjectSpec) /* can be NULL */ { CopyParams copyParams; FilterParams filterParams; FSCatalogInfo sourceCatInfo; HFSUniStr255 sourceName, tmpObjectName; FSRef tmpObjectRef, deleteMeRef; Boolean isDirectory, isReplacing = false; OSErr err = ( source != NULL && destDir != NULL ) ? noErr : paramErr; /* Zero out these two FSRefs in case an error occurs before or */ /* inside FSCopyObjectPreflight. Paranoia mainly... */ BlockZero( &deleteMeRef, sizeof( FSRef ) ); BlockZero( &tmpObjectRef, sizeof( FSRef ) ); /* setup filterParams */ filterParams.whichInfo = whichInfo; filterParams.filterProcPtr = filterProcPtr; filterParams.wantSpec = ( filterProcPtr && wantFSSpec ); /* only get this info if */ filterParams.wantName = ( filterProcPtr && wantName ); /* a filterProc is provied */ filterParams.yourDataPtr = yourDataPtr; /* Get and store away the name of the source object */ /* and setup the initial name of the new object */ if( err == noErr ) err = GetObjectName( source, &sourceName, NULL ); if( err == noErr ) tmpObjectName = (newObjectName != NULL) ? *newObjectName : sourceName; if( err == noErr ) /* preflight/prep the destination and our internal variables */ err = FSCopyObjectPreflight( source, destDir, dupeAction, &sourceCatInfo, ©Params, &tmpObjectName, &deleteMeRef, &isReplacing, &isDirectory ); /* now that we have some info, lets print it */ if( err == noErr ) { dwarning(( "%s -- err: %d, maxLevels: %u, whichInfo: %08x,\n", __FUNCTION__, err, (unsigned int)maxLevels, (int)whichInfo )); dwarning(( "\t\t\t\tdupeAction: %s, wantSpec: %s, wantName: %s,\n", ((dupeAction == kDupeActionReplace) ? "replace" : ((dupeAction == kDupeActionRename) ? "rename" : "standard")), (filterParams.wantSpec)?"yes":"no", (filterParams.wantName)?"yes":"no" )); dwarning(( "\t\t\t\tfilterProcPtr: 0x%08x, yourDataPtr: 0x%08x,\n", (unsigned int)filterProcPtr, (unsigned int)yourDataPtr )); dwarning(( "\t\t\t\tnewObjectRef: 0x%08x, newObjectSpec: 0x%08x,\n", (unsigned int)newObjectRef, (unsigned int)newObjectSpec )); dwarning(( "\t\t\t\tcopyBufferSize: %dkB, isDirectory: %s, isLocal: %s,\n", (int)copyParams.copyBufferSize/1024, (isDirectory)?"yes":"no", (copyParams.copyingToLocalVolume)?"yes":"no" )); dwarning(( "\t\t\t\tisDropBox: %s, PBHCopyFileSync supported: %s\n\n", (copyParams.copyingToDropFolder)?"yes":"no", (copyParams.volHasCopyFile)?"yes":"no" )); } if( err == noErr ) /* now copy the file/folder... */ { /* is it a folder? */ if ( isDirectory ) { /* yes */ err = CheckForDestInsideSrc(source, destDir); if( err == noErr ) err = FSCopyFolder( source, destDir, &sourceCatInfo, &tmpObjectName, ©Params, &filterParams, maxLevels, &tmpObjectRef, newObjectSpec ); } else /* no */ err = FSCopyFile(source, destDir, &sourceCatInfo, &tmpObjectName, ©Params, &filterParams, &tmpObjectRef, newObjectSpec); } /* if an object existed in the destination with the same name as */ /* the source and the caller wants to replace it, we had renamed it */ /* to ".DeleteMe" earlier. If no errors, we delete it, else delete */ /* the one we just created and rename the origenal back to its */ /* origenal name. */ /* */ /* This is done mainly to cover the case of the source being in the */ /* destination directory when kDupeActionReplace is selected */ /* (3188701) */ if( copyParams.dupeAction == kDupeActionReplace && isReplacing == true ) { dwarning(("%s -- Cleaning up, this might take a moment. err : %d\n", __FUNCTION__, err)); if( err == noErr ) err = FSDeleteObjects( &deleteMeRef ); else { /* not much we can do if the delete or rename fails, we need to preserve */ /* the origenal error code that got us here. */ /* */ /* If an error occurs before or inside SetupDestination, newFileRef and */ /* deleteMeRef will be invalid so the delete and rename will simply fail */ /* leaving the source and destination unchanged */ myverify_noerr( FSDeleteObjects( &tmpObjectRef ) ); myverify_noerr( FSRenameUnicode( &deleteMeRef, sourceName.length, sourceName.unicode, sourceCatInfo.textEncodingHint, NULL ) ); } } if( err == noErr && newObjectRef != NULL ) *newObjectRef = tmpObjectRef; /* Clean up for space and safety... Who me? */ if( copyParams.copyBuffer != NULL ) DisposePtr((char*)copyParams.copyBuffer); mycheck_noerr( err ); return err; } /*****************************************************************************/ /* Does a little preflighting (as the name suggests) to figure out the optimal */ /* buffer size, if its a drop box, on a remote volume etc */ static OSErr FSCopyObjectPreflight( const FSRef *source, const FSRef *destDir, const DupeAction dupeAction, FSCatalogInfo *sourceCatInfo, CopyParams *copyParams, HFSUniStr255 *newObjectName, FSRef *deleteMeRef, Boolean *isReplacing, Boolean *isDirectory) { GetVolParmsInfoBuffer srcVolParms, destVolParms; UInt32 srcVolParmsSize = 0, destVolParmsSize = 0; FSVolumeRefNum srcVRefNum, destVRefNum; OSErr err = ( source != NULL && destDir != NULL && sourceCatInfo != NULL && copyParams != NULL && newObjectName != NULL && deleteMeRef != NULL && isDirectory != NULL ) ? noErr : paramErr; BlockZero( copyParams, sizeof( CopyParams ) ); copyParams->dupeAction = dupeAction; if( err == noErr ) /* Get the info we will need later about the source object */ err = FSGetCatalogInfo( source, kFSCatInfoSettableInfo, sourceCatInfo, NULL, NULL, NULL ); if( err == noErr ) /* get the source's vRefNum */ err = FSGetVRefNum( source, &srcVRefNum ); if( err == noErr ) /* get the source's volParams */ err = FSGetVolParms( srcVRefNum, sizeof(GetVolParmsInfoBuffer), &srcVolParms, &srcVolParmsSize ); if( err == noErr ) /* get the destination's vRefNum */ err = FSGetVRefNum( destDir, &destVRefNum ); if( err == noErr ) { /* Calculate the optimal copy buffer size for the src vol */ copyParams->copyBufferSize = CalcBufferSizeForVol( &srcVolParms, srcVolParmsSize ); /* if src and dest on different volumes, get its vol parms */ /* and calculate its optimal buffer size */ /* else destVolParms = srcVolParms */ if( srcVRefNum != destVRefNum ) { err = FSGetVolParms( destVRefNum, sizeof(GetVolParmsInfoBuffer), &destVolParms, &destVolParmsSize ); if( err == noErr ) { ByteCount tmpBufferSize = CalcBufferSizeForVol( &destVolParms, destVolParmsSize ); if( tmpBufferSize < copyParams->copyBufferSize ) copyParams->copyBufferSize = tmpBufferSize; } } else destVolParms = srcVolParms; } if( err == noErr ) err = ((copyParams->copyBuffer = NewPtr( copyParams->copyBufferSize )) != NULL ) ? noErr : MemError(); /* figure out if source is a file or folder */ /* if it is on a local volume, */ /* if destination is a drop box */ /* if source and dest are on same server */ /* and if it supports PBHCopyFile */ if( err == noErr ) /* is the destination a Drop Box */ err = IsDropBox( destDir, ©Params->copyingToDropFolder ); if( err == noErr ) { /* Is it a directory */ *isDirectory = ((sourceCatInfo->nodeFlags & kFSNodeIsDirectoryMask) != 0); /* destVolParms.vMServerAdr is non-zero for remote volumes */ copyParams->copyingToLocalVolume = (destVolParms.vMServerAdr == 0); if( !copyParams->copyingToLocalVolume ) { /* If the destination is on a remote volume, and source and dest are on */ /* the same server, then it might support PBHCopyFileSync */ /* If not, then PBHCopyFileSync won't work */ /* figure out if the volumes support PBHCopyFileSync */ copyParams->volHasCopyFile = ( err == noErr && destVolParms.vMServerAdr == srcVolParms.vMServerAdr ) ? VolHasCopyFile(&srcVolParms) : false; } } if( err == noErr ) err = SetupDestination( destDir, copyParams->dupeAction, newObjectName, deleteMeRef, isReplacing ); return err; } #pragma mark ----- Copy Files ----- /*****************************************************************************/ static OSErr FSCopyFile( const FSRef *source, const FSRef *destDir, const FSCatalogInfo *sourceCatInfo, const HFSUniStr255 *newFileName, CopyParams *copyParams, FilterParams *filterParams, FSRef *outFileRef, FSSpec *outFileSpec ) { FSCatalogInfo catInfo = *sourceCatInfo; FSRef newFileRef; FSSpec newFileSpec; OSErr err = ( source != NULL && destDir != NULL && copyParams != NULL && filterParams != NULL ) ? noErr : paramErr; /* If you would like a Pre-Copy filter (i.e to weed out objects */ /* you don't want to copy) you should add it here */ if( err == noErr ) /* copy the file over */ err = CopyFile( source, &catInfo, destDir, newFileName, copyParams, &newFileRef, (filterParams->wantSpec || outFileSpec) ? &newFileSpec : NULL ); /* Call the IterateFilterProc _after_ the new file was created even if an error occured. */ /* Note: if an error occured above, the FSRef and other info might not be valid */ if( filterParams->filterProcPtr != NULL ) { /* get the extra info the user wanted on the new file that we don't have */ if( err == noErr && (filterParams->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone ) err = FSGetCatalogInfo( &newFileRef, filterParams->whichInfo & ~kFSCatInfoSettableInfo, &catInfo, NULL, NULL, NULL ); err = CallCopyObjectFilterProc( filterParams->filterProcPtr, false, 0, err, &catInfo, &newFileRef, (filterParams->wantSpec) ? &newFileSpec : NULL, (filterParams->wantName) ? newFileName : NULL, filterParams->yourDataPtr); } if( err == noErr ) { if( outFileRef != NULL ) *outFileRef = newFileRef; if( outFileSpec != NULL ) *outFileSpec = newFileSpec; } mycheck_noerr(err); return err; } /*****************************************************************************/ static OSErr CopyFile( const FSRef *source, FSCatalogInfo *sourceCatInfo, const FSRef *destDir, const HFSUniStr255 *destName, /* can be NULL */ CopyParams *params, FSRef *newFile, /* can be NULL */ FSSpec *newSpec ) /* can be NULL */ { OSErr err = paramErr; /* Clear the "inited" bit so that the Finder positions the icon for us. */ ((FInfo *)(sourceCatInfo->finderInfo))->fdFlags &= ~kHasBeenInited; /* if the volumes support PBHCopyFileSync, try to use it */ if( params->volHasCopyFile == true ) err = FSUsePBHCopyFile( source, destDir, destName, kTextEncodingUnknown, newFile, newSpec ); /* if PBHCopyFile didn't work or not supported, */ if( err != noErr ) /* then try old school file transfer */ err = DoCopyFile( source, sourceCatInfo, destDir, destName, params, newFile, newSpec ); mycheck_noerr(err); return err; } /*****************************************************************************/ /* Wrapper function for PBHCopyFileSync */ static OSErr FSUsePBHCopyFile( const FSRef *srcFileRef, const FSRef *dstDirectoryRef, const HFSUniStr255 *destName, /* can be NULL */ TextEncoding textEncodingHint, FSRef *newRef, /* can be NULL */ FSSpec *newSpec) /* can be NULL */ { FSSpec srcFileSpec; FSCatalogInfo catalogInfo; HParamBlockRec pb; Str255 hfsName; OSErr err = ( srcFileRef != NULL && dstDirectoryRef != NULL ) ? noErr : paramErr; if( err == noErr ) /* get FSSpec of source FSRef */ err = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL); if( err == noErr ) /* get the destination vRefNum and nodeID (nodeID is the dirID) */ err = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume | kFSCatInfoNodeID, &catalogInfo, NULL, NULL, NULL); if( err == noErr ) /* gather all the info needed */ { pb.copyParam.ioVRefNum = srcFileSpec.vRefNum; pb.copyParam.ioDirID = srcFileSpec.parID; pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name; pb.copyParam.ioDstVRefNum = catalogInfo.volume; pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID; pb.copyParam.ioNewName = NULL; if( destName != NULL ) err = UniStrToPStr( destName, textEncodingHint, false, hfsName ); pb.copyParam.ioCopyName = ( destName != NULL && err == noErr ) ? hfsName : NULL; } if( err == noErr ) /* tell the server to copy the object */ err = PBHCopyFileSync(&pb); if( err == noErr ) { if( newSpec != NULL ) /* caller wants an FSSpec, so make it */ myverify_noerr(FSMakeFSSpec( pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, pb.copyParam.ioCopyName, newSpec)); if( newRef != NULL ) /* caller wants an FSRef, so make it */ myverify_noerr(FSMakeFSRef( pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, pb.copyParam.ioCopyName, newRef)); } if( err != paramErr ) /* returning paramErr is ok, it means PBHCopyFileSync was not supported */ mycheck_noerr(err); return err; } /*****************************************************************************/ /* Copies a file referenced by source to the directory referenced by */ /* destDir. destName is the name the file we are going to copy to the */ /* destination. sourceCatInfo is the catalog info of the file, which */ /* is passed in as an optimization (we could get it by doing a */ /* FSGetCatalogInfo but the caller has already done that so we might as */ /* well take advantage of that). */ /* */ static OSErr DoCopyFile(const FSRef *source, FSCatalogInfo *sourceCatInfo, const FSRef *destDir, const HFSUniStr255 *destName, CopyParams *params, FSRef *newRef, FSSpec *newSpec ) { FSRef dest; FSSpec tmpSpec; FSPermissionInfo originalPermissions; OSType originalFileType = 'xxxx'; UInt16 originalNodeFlags = kFSCatInfoNone; Boolean getSpec; OSErr err = noErr; /* If we're copying to a drop folder, we won't be able to reset this */ /* information once the copy is done, so we don't mess it up in */ /* the first place. We still clear the locked bit though; items dropped */ /* into a drop folder always become unlocked. */ if (!params->copyingToDropFolder) { /* Remember to clear the file's type, so the Finder doesn't */ /* look at the file until we're done. */ originalFileType = ((FInfo *) &sourceCatInfo->finderInfo)->fdType; ((FInfo *) &sourceCatInfo->finderInfo)->fdType = kFirstMagicBusyFiletype; /* Remember and clear the file's locked status, so that we can */ /* actually write the forks we're about to create. */ originalNodeFlags = sourceCatInfo->nodeFlags; } sourceCatInfo->nodeFlags &= ~kFSNodeLockedMask; /* figure out if we should get the FSSpec to the new file or not */ /* If the caller asked for it, or if we need it for symlinks */ getSpec = ( ( newSpec != NULL ) || ( !params->copyingToDropFolder && originalFileType == 'slnk' && ((FInfo *) &sourceCatInfo->finderInfo)->fdCreator == 'rhap' ) ); /* we need to have user level read/write/execute access to the file we are */ /* going to create otherwise FSCreateFileUnicode will return */ /* -5000 (afpAccessDenied), and the FSRef returned will be invalid, yet */ /* the file is created (size 0k)... bug? */ originalPermissions = *((FSPermissionInfo*)sourceCatInfo->permissions); ((FSPermissionInfo*)sourceCatInfo->permissions)->mode |= kRWXUserAccessMask; /* Classic only supports 9.1 and higher, so we don't have to worry */ /* about 2397324 */ if( err == noErr ) err = FSCreateFileUnicode(destDir, destName->length, destName->unicode, kFSCatInfoSettableInfo, sourceCatInfo, &dest, ( getSpec ) ? &tmpSpec : NULL ); if( err == noErr ) /* Copy the forks over to the new file */ err = CopyForks(source, &dest, params); /* Restore the original file type, creation and modification dates, */ /* locked status and permissions. */ /* This is one of the places where we need to handle drop */ /* folders as a special case because this FSSetCatalogInfo will fail for */ /* an item in a drop folder, so we don't even attempt it. */ if (err == noErr && !params->copyingToDropFolder) { ((FInfo *) &sourceCatInfo->finderInfo)->fdType = originalFileType; sourceCatInfo->nodeFlags = originalNodeFlags; *((FSPermissionInfo*)sourceCatInfo->permissions) = originalPermissions; /* 2796751, FSSetCatalogInfo returns -36 when setting the Finder Info */ /* for a symlink. To workaround this, when the file is a */ /* symlink (slnk/rhap) we will finish the copy in two steps. First */ /* setting everything but the Finder Info on the file, then calling */ /* FSpSetFInfo to set the Finder Info for the file. I would rather use */ /* an FSRef function to set the Finder Info, but FSSetCatalogInfo is */ /* the only one... catch-22... */ /* */ /* The Carbon File Manager always sets the type/creator of a symlink to */ /* slnk/rhap if the file is a symlink we do the two step, if it isn't */ /* we use FSSetCatalogInfo to do all the work. */ if ((originalFileType == 'slnk') && (((FInfo *) &sourceCatInfo->finderInfo)->fdCreator == 'rhap')) { /* Its a symlink */ /* set all the info, except the Finder info */ err = FSSetCatalogInfo(&dest, kFSCatInfoNodeFlags | kFSCatInfoPermissions, sourceCatInfo); if ( err == noErr ) /* set the Finder Info to that file */ err = FSpSetFInfo( &tmpSpec, ((FInfo *) &sourceCatInfo->finderInfo) ); } else /* its a regular file */ err = FSSetCatalogInfo(&dest, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo | kFSCatInfoPermissions, sourceCatInfo); } /* If we created the file and the copy failed, try to clean up by */ /* deleting the file we created. We do this because, while it's */ /* possible for the copy to fail halfway through and the File Manager */ /* doesn't really clean up that well in that case, we *really* don't want */ /* any half-created files being left around. */ /* if the file already existed, we don't want to delete it */ if( err == noErr || err == dupFNErr ) { /* if everything was fine, then return the new file Spec/Ref */ if( newRef != NULL ) *newRef = dest; if( newSpec != NULL ) *newSpec = tmpSpec; } else myverify_noerr( FSDeleteObjects(&dest) ); mycheck_noerr(err); return err; } /*****************************************************************************/ #pragma mark ----- Copy Folders ----- static OSErr FSCopyFolder( const FSRef *source, const FSRef *destDir, const FSCatalogInfo *sourceCatInfo, const HFSUniStr255 *newObjectName, CopyParams *copyParams, FilterParams *filterParams, ItemCount maxLevels, FSRef *outDirRef, FSSpec *outDirSpec ) { FSCopyFolderGlobals folderGlobals; FolderListData *tmpListData = NULL; FSCatalogInfo catInfo = *sourceCatInfo; FSRef newDirRef; FSSpec newDirSpec; OSErr err; /* setup folder globals */ folderGlobals.catInfoList = (FSCatalogInfo*) NewPtr( sizeof( FSCatalogInfo ) * kNumObjects ); folderGlobals.srcRefList = (FSRef*) NewPtr( sizeof( FSRef ) * kNumObjects ); folderGlobals.nameList = (HFSUniStr255*) NewPtr( sizeof( HFSUniStr255 ) * kNumObjects ); folderGlobals.folderListIter = NULL; folderGlobals.copyParams = copyParams; folderGlobals.filterParams = filterParams; folderGlobals.maxLevels = maxLevels; folderGlobals.currentLevel = 0; /* if any of the NewPtr calls failed, we MUST bail */ err = ( folderGlobals.catInfoList != NULL && folderGlobals.srcRefList != NULL && folderGlobals.nameList != NULL ) ? noErr : memFullErr; /* init the linked list we will use to keep track of the folders */ InitLinkedList( &folderGlobals.folderList, MyDisposeDataProc ); if( err == noErr && !copyParams->copyingToDropFolder ) err = GetMagicBusyCreateDate( &catInfo.createDate ); if( err == noErr ) /* create the directory */ err = DoCreateFolder( source, destDir, &catInfo, newObjectName, folderGlobals.copyParams, &newDirRef, (filterParams->wantSpec || outDirSpec ) ? &newDirSpec : NULL ); /* Note: if an error occured above, the FSRef and other info might not be valid */ if( filterParams->filterProcPtr != NULL ) { /* get the info the user wanted about the source directory we don't have */ if( err == noErr && (filterParams->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone ) err = FSGetCatalogInfo(&newDirRef, filterParams->whichInfo & ~kFSCatInfoSettableInfo, &catInfo, NULL, NULL, NULL); err = CallCopyObjectFilterProc(filterParams->filterProcPtr, false, folderGlobals.currentLevel, err, &catInfo, &newDirRef, ( filterParams->wantSpec ) ? &newDirSpec : NULL, ( filterParams->wantName ) ? newObjectName : NULL, filterParams->yourDataPtr); } if( err == noErr ) /* create the memory for this folder */ err = ( ( tmpListData = (FolderListData*) NewPtr( sizeof( FolderListData ) ) ) != NULL ) ? noErr : MemError(); if( err == noErr ) { /* setup the folder info */ tmpListData->sourceDirRef = *source; tmpListData->destDirRef = newDirRef; tmpListData->level = folderGlobals.currentLevel; /* add this folder to the list to give ProcessFolderList something to chew on */ err = AddToTail( &folderGlobals.folderList, tmpListData ); if( err == noErr ) /* tmpListData added successfully */ err = ProcessFolderList( &folderGlobals ); else /* error occured, so dispose of memory */ DisposePtr( (char*) tmpListData ); } dwarning(("\n%s -- %u folders were found\n", __FUNCTION__, (unsigned int)GetNumberOfItems( &folderGlobals.folderList ) )); /* when we're done destroy the list and free up any memory we allocated */ DestroyList( &folderGlobals.folderList ); /* now that the copy is complete, we can set things back to normal */ /* for the directory we just created. */ /* We have to do this only for the top directory of the copy */ /* all subdirectories were created all at once */ if( err == noErr && !folderGlobals.copyParams->copyingToDropFolder ) err = FSSetCatalogInfo( &newDirRef, kFSCatInfoCreateDate | kFSCatInfoPermissions, sourceCatInfo ); /* Copy went as planned, and caller wants an FSRef/FSSpec to the new directory */ if( err == noErr ) { if( outDirRef != NULL) *outDirRef = newDirRef; if( outDirSpec != NULL ) *outDirSpec = newDirSpec; } /* clean up for space and safety, who me? */ if( folderGlobals.catInfoList ) DisposePtr( (char*) folderGlobals.catInfoList ); if( folderGlobals.srcRefList ) DisposePtr( (char*) folderGlobals.srcRefList ); if( folderGlobals.nameList ) DisposePtr( (char*) folderGlobals.nameList ); mycheck_noerr(err); return ( err ); } /*****************************************************************************/ /* We now store a list of all the folders/subfolders we encounter in the source */ /* Each node in the list contains an FSRef to the source, an FSRef to the */ /* mirror folder in the destination, and the level in the source that folder */ /* is on. This is done so that we can use FSGetCatalogInfoBulk to its full */ /* potential (getting items in bulk). We copy the source one folder at a time. */ /* Copying over the contents of each folder before we continue on to the next */ /* folder in the list. This allows us to use the File Manager's own caching */ /* system to our advantage. */ static OSErr ProcessFolderList( FSCopyFolderGlobals *folderGlobals ) { FolderListData *folderListData; OSErr err = noErr; /* iterate through the list of folders and copy over each one individually */ for( InitIterator( &folderGlobals->folderList, &folderGlobals->folderListIter ); folderGlobals->folderListIter != NULL && err == noErr; Next( &folderGlobals->folderListIter ) ) { /* Get the data for this folder */ folderListData = (FolderListData*) GetData( folderGlobals->folderListIter ); if( folderListData != NULL ) { #if DEBUG && !TARGET_API_MAC_OS8 { char path[1024]; myverify_noerr(FSRefMakePath( &(folderListData->sourceDirRef), (unsigned char*)path, 1024 )); dwarning(("\n\n%s -- Copying contents of\n\t%s\n", __FUNCTION__, path)); myverify_noerr(FSRefMakePath( &(folderListData->destDirRef), (unsigned char*)path, 1024 )); dwarning(("\t\tto\n\t%s\n", path)); } #endif /* stuff the data into our globals */ folderGlobals->sourceDirRef = &(folderListData->sourceDirRef); folderGlobals->destDirRef = &(folderListData->destDirRef); folderGlobals->currentLevel = folderListData->level; /* Copy over this folder and add any subfolders to our list of folders */ /* so they will get processed later */ err = CopyFolder( folderGlobals ); } } return err; } /*****************************************************************************/ /* Copy the contents of the source into the destination. If any subfolders */ /* are found, add them to a local list of folders during the loop stage */ /* Once the copy is done, insert the local list into the global list right */ /* after the current position in the list. This is done so we don't jump */ /* all over the disk getting the different folders to copy */ static OSErr CopyFolder( FSCopyFolderGlobals *folderGlobals ) { GenLinkedList tmpList; FolderListData *tmpListData = NULL; FilterParams *filterPtr = folderGlobals->filterParams; FSIterator iterator; FSRef newRef; FSSpec newSpec; UInt32 actualObjects; OSErr err, junkErr; int i; /* Init the local list */ InitLinkedList( &tmpList, MyDisposeDataProc); err = FSOpenIterator( folderGlobals->sourceDirRef, kFSIterateFlat, &iterator ); if( err == noErr ) { do { /* grab a bunch of objects (kNumObjects) from this folder and copy them over */ err = FSGetCatalogInfoBulk( iterator, kNumObjects, &actualObjects, &filterPtr->containerChanged, kFSCatInfoSettableInfo, folderGlobals->catInfoList, folderGlobals->srcRefList, NULL, folderGlobals->nameList ); if( ( err == noErr || err == errFSNoMoreItems ) && ( actualObjects != 0 ) ) { dwarning(("%s -- actualObjects retrieved from FSGetCatalogInfoBulk: %u\n",__FUNCTION__, (unsigned int)actualObjects )); /* iterate over the objects actually returned */ for( i = 0; i < actualObjects; i++ ) { /* Any errors in here will be passed to the filter proc */ /* we don't want an error in here to prematurely cancel the copy */ /* If you would like a Pre-Copy filter (i.e to weed out objects */ /* you don't want to copy) you should add it here */ /* Is the new object a directory? */ if( ( folderGlobals->catInfoList[i].nodeFlags & kFSNodeIsDirectoryMask ) != 0 ) { /* yes */ junkErr = CreateFolder( &folderGlobals->srcRefList[i], folderGlobals->destDirRef, &folderGlobals->catInfoList[i], &folderGlobals->nameList[i], folderGlobals->copyParams, &newRef, (filterPtr->wantSpec) ? &newSpec : NULL ); /* If maxLevels is zero, we aren't checking levels */ /* If currentLevel+1 < maxLevels, add this folder to the list */ if( folderGlobals->maxLevels == 0 || (folderGlobals->currentLevel + 1) < folderGlobals->maxLevels ) { if( junkErr == noErr ) /* Create memory for folder list data */ junkErr = ( ( tmpListData = (FolderListData*) NewPtr( sizeof( FolderListData ) ) ) != NULL ) ? noErr : MemError(); if( junkErr == noErr ) { /* Setup the folder list data */ tmpListData->sourceDirRef = folderGlobals->srcRefList[i]; tmpListData->destDirRef = newRef; tmpListData->level = folderGlobals->currentLevel + 1; /* Add it to the local list */ junkErr = AddToTail( &tmpList, tmpListData ); } /* If an error occured and memory was created, we need to dispose of it */ /* since it was not added to the list */ if( junkErr != noErr && tmpListData != NULL ) DisposePtr( (char*) tmpListData ); } } else { /* no */ junkErr = CopyFile( &folderGlobals->srcRefList[i], &folderGlobals->catInfoList[i], folderGlobals->destDirRef, &folderGlobals->nameList[i], folderGlobals->copyParams, &newRef, ( filterPtr->wantSpec ) ? &newSpec : NULL ); } /* Note: if an error occured above, the FSRef and other info might not be valid */ if( filterPtr->filterProcPtr != NULL ) { if( junkErr == noErr && (filterPtr->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone ) /* get the extra info about the new object that the user wanted that we don't already have */ junkErr = FSGetCatalogInfo( &newRef, filterPtr->whichInfo & ~kFSCatInfoSettableInfo, &folderGlobals->catInfoList[i], NULL, NULL, NULL ); err = CallCopyObjectFilterProc( filterPtr->filterProcPtr, filterPtr->containerChanged, folderGlobals->currentLevel, junkErr, &folderGlobals->catInfoList[i], &newRef, ( filterPtr->wantSpec ) ? &newSpec : NULL, ( filterPtr->wantName ) ? &folderGlobals->nameList[i] : NULL, filterPtr->yourDataPtr); } } } }while( err == noErr ); /* errFSNoMoreItems is OK - it only means we hit the end of this level */ /* afpAccessDenied is OK too - it only means we cannot see inside the directory */ if( err == errFSNoMoreItems || err == afpAccessDenied ) err = noErr; /* Insert the local list of folders from the current folder into our global list. Even */ /* if no items were added to the local list (due to error, or empty folder), InsertList */ /* handles it correctly. We add the local list even if an error occurred. It will get */ /* disposed of when the global list is destroyed. Doesn't hurt to have a couple extra */ /* steps when we're going to bail anyways. */ InsertList( &folderGlobals->folderList, &tmpList, folderGlobals->folderListIter ); /* Close the FSIterator (closing an open iterator should never fail) */ (void) FSCloseIterator(iterator); } mycheck_noerr( err ); return err; } /*****************************************************************************/ /* Determines whether the destination directory is equal to the source */ /* item, or whether it's nested inside the source item. Returns a */ /* errFSDestInsideSource if that's the case. We do this to prevent */ /* endless recursion while copying. */ /* */ static OSErr CheckForDestInsideSrc( const FSRef *source, const FSRef *destDir) { FSRef thisDir = *destDir; FSCatalogInfo thisDirInfo; Boolean done = false; OSErr err; do { err = FSCompareFSRefs(source, &thisDir); if (err == noErr) err = errFSDestInsideSource; else if (err == diffVolErr) { err = noErr; done = true; } else if (err == errFSRefsDifferent) { /* This is somewhat tricky. We can ask for the parent of thisDir */ /* by setting the parentRef parameter to FSGetCatalogInfo but, if */ /* thisDir is the volume's FSRef, this will give us back junk. */ /* So we also ask for the parent's dir ID to be returned in the */ /* FSCatalogInfo record, and then check that against the node */ /* ID of the root's parent (ie 1). If we match that, we've made */ /* it to the top of the hierarchy without hitting source, so */ /* we leave with no error. */ err = FSGetCatalogInfo(&thisDir, kFSCatInfoParentDirID, &thisDirInfo, NULL, NULL, &thisDir); if( ( err == noErr ) && ( thisDirInfo.parentDirID == fsRtParID ) ) done = true; } } while ( err == noErr && ! done ); mycheck_noerr( err ); return err; } /*****************************************************************************/ #pragma mark ----- Copy Forks ----- /* This is where the majority of the work is done. I special cased */ /* DropBoxes in order to use FSIterateForks to its full potential for */ /* the more common case (read/write permissions). It also simplifies */ /* the code to have it seperate. */ static OSErr CopyForks( const FSRef *source, const FSRef *dest, CopyParams *params) { OSErr err; err = ( !params->copyingToDropFolder ) ? CopyForksToDisk ( source, dest, params ) : CopyForksToDropBox ( source, dest, params ); mycheck_noerr( err ); return err; } /* Open each fork individually and copy them over to the destination */ static OSErr CopyForksToDisk( const FSRef *source, const FSRef *dest, CopyParams *params ) { HFSUniStr255 forkName; CatPositionRec iterator; SInt64 forkSize; SInt16 srcRefNum, destRefNum; OSErr err; /* need to initialize the iterator before using it */ iterator.initialize = 0; do { err = FSIterateForks( source, &iterator, &forkName, &forkSize, NULL ); /* Create the fork. Note: Data and Resource forks are automatically */ /* created when the file is created. FSCreateFork returns noErr for them */ /* We also want to create the fork even if there is no data to preserve */ /* empty forks */ if( err == noErr ) err = FSCreateFork( dest, forkName.length, forkName.unicode ); /* Mac OS 9.0 has a bug (in the AppleShare external file system, */ /* I think) [2410374] that causes FSCreateFork to return an errFSForkExists */ /* error even though the fork is empty. The following code swallows */ /* the error (which is harmless) in that case. */ if( err == errFSForkExists && !params->copyingToLocalVolume ) err = noErr; /* The remainder of this code only applies if there is actual data */ /* in the source fork. */ if( err == noErr && forkSize > 0 ) { destRefNum = srcRefNum = 0; /* Open the destination fork */ err = FSOpenFork(dest, forkName.length, forkName.unicode, fsWrPerm, &destRefNum); if( err == noErr ) /* Open the source fork */ err = FSOpenFork(source, forkName.length, forkName.unicode, fsRdPerm, &srcRefNum); if( err == noErr ) /* Write the fork to disk */ err = WriteFork( srcRefNum, destRefNum, params, forkSize ); if( destRefNum != 0 ) /* Close the destination fork */ myverify_noerr( FSCloseFork( destRefNum ) ); if( srcRefNum != 0 ) /* Close the source fork */ myverify_noerr( FSCloseFork( srcRefNum ) ); } } while( err == noErr ); if( err == errFSNoMoreItems ) err = noErr; mycheck_noerr( err ); return err; } /* If we're copying to a DropBox, we have to handle the copy process a little */ /* differently then when we are copying to a regular folder. */ static OSErr CopyForksToDropBox( const FSRef *source, const FSRef *dest, CopyParams *params ) { GenLinkedList forkList; GenIteratorPtr pIter; ForkTrackerPtr forkPtr; SInt16 srcRefNum; OSErr err; InitLinkedList( &forkList, MyCloseForkProc ); /* If we're copying into a drop folder, open up all of those forks. */ /* We have to do this because once we've started writing to a fork */ /* in a drop folder, we can't open any more forks. */ err = OpenAllForks( dest, &forkList ); /* Copy each fork over to the destination */ for( InitIterator( &forkList, &pIter ); pIter != NULL && err == noErr; Next( &pIter ) ) { srcRefNum = 0; forkPtr = GetData( pIter ); /* Open the source fork */ err = FSOpenFork(source, forkPtr->forkName.length, forkPtr->forkName.unicode, fsRdPerm, &srcRefNum); if( err == noErr ) /* Write the data over */ err = WriteFork( srcRefNum, forkPtr->forkDestRefNum, params, forkPtr->forkSize ); if( srcRefNum != 0 ) /* Close the source fork */ myverify_noerr( FSCloseFork( srcRefNum ) ); } /* we're done, so destroy the list even if an error occured */ /* the DisposeDataProc will close any open forks */ DestroyList( &forkList ); mycheck_noerr( err ); return err; } /*****************************************************************************/ /* Create and open all the forks in the destination file. We need to do this when */ /* we're copying into a drop folder, where you must open all the forks before starting */ /* to write to any of them. */ /* */ /* IMPORTANT: If it fails, this routine won't close forks that opened successfully. */ /* Make sure that the DisposeDataProc for the forkList closed any open forks */ /* Or you close each one manually before destroying the list */ static OSErr OpenAllForks( const FSRef *dest, GenLinkedList *forkList ) { ForkTrackerPtr forkPtr; HFSUniStr255 forkName; CatPositionRec iterator; SInt64 forkSize; OSErr err = ( dest != NULL && forkList != NULL ) ? noErr : paramErr; /* need to initialize the iterator before using it */ iterator.initialize = 0; /* Iterate over the list of forks */ while( err == noErr ) { forkPtr = NULL; /* init forkPtr */ err = FSIterateForks( dest, &iterator, &forkName, &forkSize, NULL ); if( err == noErr ) err = ( forkPtr = (ForkTrackerPtr) NewPtr( sizeof( ForkTracker ) ) ) != NULL ? noErr : MemError(); if( err == noErr ) { forkPtr->forkName = forkName; forkPtr->forkSize = forkSize; forkPtr->forkDestRefNum = 0; /* Create the fork. Note: Data and Resource forks are automatically */ /* created when the file is created. FSCreateFork returns noErr for them */ /* We also want to create the fork even if there is no data to preserve */ /* empty forks */ err = FSCreateFork( dest, forkName.length, forkName.unicode ); /* Swallow afpAccessDenied because this operation causes the external file */ /* system compatibility shim in Mac OS 9 to generate a GetCatInfo request */ /* to the AppleShare external file system, which in turn causes an AFP */ /* GetFileDirParms request on the wire, which the AFP server bounces with */ /* afpAccessDenied because the file is in a drop folder. As there's no */ /* native support for non-classic forks in current AFP, there's no way I */ /* can decide how I should handle this in a non-test case. So I just */ /* swallow the error and hope that when native AFP support arrives, the */ /* right thing will happen. */ if( err == afpAccessDenied ) err = noErr; /* only open the fork if the fork has some data */ if( err == noErr && forkPtr->forkSize > 0 ) err = FSOpenFork( dest, forkPtr->forkName.length, forkPtr->forkName.unicode, fsWrPerm, &forkPtr->forkDestRefNum ); /* if everything is ok, add this fork to the list */ if( err == noErr ) err = AddToTail( forkList, forkPtr ); } if( err != noErr && forkPtr != NULL ) DisposePtr... [truncated message content] |