FSCopyObject.c

Go to the documentation of this file.
00001 /*
00002         File:           FSCopyObject.c
00003         
00004         Contains:       A Copy/Delete Files/Folders engine which uses HFS+ API's.
00005                                 This code takes some tricks/techniques from MoreFilesX and
00006                                 MPFileCopy, wraps them all up into an easy to use API, and
00007                                 adds a bunch of features.  It will run on Mac OS 9.1 through 
00008                                 9.2.x and 10.1.x and up (Classic, Carbon and Mach-O)
00009 
00010         Disclaimer:     IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
00011                                 ("Apple") in consideration of your agreement to the following terms, and your
00012                                 use, installation, modification or redistribution of this Apple software
00013                                 constitutes acceptance of these terms.  If you do not agree with these terms,
00014                                 please do not use, install, modify or redistribute this Apple software.
00015 
00016                                 In consideration of your agreement to abide by the following terms, and subject
00017                                 to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
00018                                 copyrights in this original Apple software (the "Apple Software"), to use,
00019                                 reproduce, modify and redistribute the Apple Software, with or without
00020                                 modifications, in source and/or binary forms; provided that if you redistribute
00021                                 the Apple Software in its entirety and without modifications, you must retain
00022                                 this notice and the following text and disclaimers in all such redistributions of
00023                                 the Apple Software.  Neither the name, trademarks, service marks or logos of
00024                                 Apple Computer, Inc. may be used to endorse or promote products derived from the
00025                                 Apple Software without specific prior written permission from Apple.  Except as
00026                                 expressly stated in this notice, no other rights or licenses, express or implied,
00027                                 are granted by Apple herein, including but not limited to any patent rights that
00028                                 may be infringed by your derivative works or by other works in which the Apple
00029                                 Software may be incorporated.
00030 
00031                                 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
00032                                 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
00033                                 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00034                                 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
00035                                 COMBINATION WITH YOUR PRODUCTS.
00036 
00037                                 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
00038                                 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00039                                 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00040                                 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
00041                                 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
00042                                 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
00043                                 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00044 
00045         Copyright © 2002-2004 Apple Computer, Inc., All Rights Reserved
00046 */
00047 
00048 #include "FSCopyObject.h"
00049 #include "GenLinkedList.h"
00050 #if !TARGET_API_MAC_OSX
00051 #include <UnicodeConverter.h>
00052 #endif
00053 #include <stddef.h>
00054 #include <string.h>
00055 
00056 #pragma mark ----- Tunable Parameters -----
00057 
00058 /* The following constants control the behavior of the copy engine. */
00059 
00060 enum {          /* BufferSizeForVolSpeed */
00061 /*      kDefaultCopyBufferSize  =   2L * 1024 * 1024,*/                 /* 2MB,   Fast but not very responsive. */
00062         kDefaultCopyBufferSize  = 256L * 1024,                                  /* 256kB, Slower but can still use machine. */
00063         kMaximumCopyBufferSize  =   2L * 1024 * 1024,
00064         kMinimumCopyBufferSize  = 1024
00065 };
00066 
00067 enum {          /* CheckForDestInsideSrc */
00068         errFSDestInsideSource   = -1234
00069 };
00070 
00071 enum {          
00072                         /* for use with PBHGetDirAccess in IsDropBox */
00073         kPrivilegesMask                 = kioACAccessUserWriteMask | kioACAccessUserReadMask | kioACAccessUserSearchMask,
00074 
00075                         /* for use with FSGetCatalogInfo and FSPermissionInfo->mode                     */
00076                         /* from sys/stat.h...  note -- sys/stat.h definitions are in octal      */
00077                         /*                                                                                                                                      */
00078                         /* You can use these values to adjust the users/groups permissions      */
00079                         /* on a file/folder with FSSetCatalogInfo and extracting the            */
00080                         /* kFSCatInfoPermissions field.  See code below for examples            */
00081         kRWXUserAccessMask              = 0x01C0,
00082         kReadAccessUser                 = 0x0100,
00083         kWriteAccessUser                = 0x0080,
00084         kExecuteAccessUser              = 0x0040,
00085 
00086         kRWXGroupAccessMask             = 0x0038,
00087         kReadAccessGroup                = 0x0020,
00088         kWriteAccessGroup               = 0x0010,
00089         kExecuteAccessGroup             = 0x0008,
00090 
00091         kRWXOtherAccessMask             = 0x0007,
00092         kReadAccessOther                = 0x0004,
00093         kWriteAccessOther               = 0x0002,
00094         kExecuteAccessOther             = 0x0001,
00095 
00096         kDropFolderValue                = kWriteAccessOther | kExecuteAccessOther
00097 };
00098 
00099 #define kNumObjects                     80
00100 
00101 #define VolHasCopyFile(volParms)        (((volParms)->vMAttrib & (1L << bHasCopyFile)) != 0)
00102 
00103 #pragma mark ----- Struct Definitions -----
00104 
00105         /* The CopyParams data structure holds the copy buffer used     */
00106         /* when copying the forks over, as well as special case         */
00107         /* info on the destination                                                                      */
00108 struct CopyParams {
00109         void                               *copyBuffer;
00110         ByteCount                               copyBufferSize;
00111         Boolean                         copyingToDropFolder;
00112         Boolean                                 copyingToLocalVolume;
00113         Boolean                                 volHasCopyFile;
00114         DupeAction                              dupeAction;
00115 };
00116 typedef struct CopyParams CopyParams;
00117 
00118         /* The FilterParams data structure holds the date and info              */
00119         /* that the caller wants passed into the Filter Proc, as well   */
00120         /* as the Filter Proc Pointer itself                                                    */
00121 struct FilterParams {
00122         FSCatalogInfoBitmap             whichInfo;
00123         CopyObjectFilterProcPtr filterProcPtr;
00124         Boolean                                 containerChanged;
00125         Boolean                                 wantSpec;
00126         Boolean                                 wantName;
00127         void                               *yourDataPtr;
00128 };
00129 typedef struct FilterParams FilterParams;
00130 
00131         /* The ForkTracker data structure holds information about a specific fork,      */
00132         /* specifically the name and the refnum.  We use this to build a list of        */
00133         /* all the forks before we start copying them.  We need to do this because      */
00134         /* if we're copying into a drop folder, we must open all the forks before       */
00135         /* we start copying data into any of them.                                                                      */
00136         /* Plus it's a convenient way to keep track of all the forks...                         */
00137 struct ForkTracker {
00138         HFSUniStr255                    forkName;
00139         SInt64                                  forkSize;
00140         SInt16                          forkDestRefNum;
00141 };
00142 typedef struct ForkTracker ForkTracker;
00143 typedef ForkTracker *ForkTrackerPtr;
00144 
00145         /* The FolderListData data structure holds FSRefs to the source and                     */
00146         /* coorisponding destination folder, as well as which level its on                      */
00147         /* for use in ProcessFolderList.                                                                                        */ 
00148 struct FolderListData
00149 {
00150         FSRef                                   sourceDirRef;
00151         FSRef                                   destDirRef;
00152         UInt32                                  level;
00153 };
00154 typedef struct FolderListData FolderListData;
00155 
00156         /* The FSCopyFolderGlobals data structure holds the information needed to       */
00157         /* copy a directory                                                                                                                     */
00158 struct FSCopyFolderGlobals
00159 {
00160         FSRef                              *sourceDirRef;
00161         FSRef                              *destDirRef;
00162 
00163         FSCatalogInfo              *catInfoList;
00164         FSRef                              *srcRefList;
00165         HFSUniStr255               *nameList;
00166         
00167         GenLinkedList                   folderList;
00168         GenIteratorPtr                  folderListIter;
00169         
00170         CopyParams                         *copyParams;
00171         FilterParams               *filterParams;
00172         Boolean                                 containerChanged;
00173         
00174         ItemCount                               maxLevels;
00175         ItemCount                               currentLevel;
00176 };
00177 typedef struct FSCopyFolderGlobals FSCopyFolderGlobals;
00178 
00179         /* The FSDeleteObjectGlobals data structure holds information needed to */
00180         /* recursively delete a directory                                                                               */
00181 struct FSDeleteObjectGlobals
00182 {
00183         FSCatalogInfo                   catalogInfo;            /* FSCatalogInfo                                */
00184         ItemCount                               actualObjects;          /* number of objects returned   */
00185         OSErr                                   result;                         /* result                                               */
00186 };
00187 typedef struct FSDeleteObjectGlobals FSDeleteObjectGlobals;
00188 
00189 #pragma mark ----- Local Prototypes -----
00190 
00191 static OSErr    FSCopyObjectPreflight ( const FSRef                     *source,
00192                                                                                 const FSRef                     *destDir,
00193                                                                                 const DupeAction         dupeAction,
00194                                                                                 FSCatalogInfo           *sourceCatInfo,
00195                                                                                 CopyParams                      *copyParams,            /* can be NULL */
00196                                                                                 HFSUniStr255            *newObjectName,
00197                                                                                 FSRef                           *deleteMeRef,
00198                                                                                 Boolean                         *isReplacing,
00199                                                                                 Boolean                         *isDirectory );
00200 
00201 static OSErr    FSCopyFile                        (     const FSRef                     *source,
00202                                                                                 const FSRef                     *destDir,
00203                                                                                 const FSCatalogInfo     *sourceCatInfo,
00204                                                                                 const HFSUniStr255      *newFileName,
00205                                                                                 CopyParams                      *copyParams,
00206                                                                                 FilterParams            *filterParams,
00207                                                                                 FSRef                           *newFileRef,            /* can be NULL */
00208                                                                                 FSSpec                          *newFileSpec );         /* can be NULL */
00209                                                                 
00210 static OSErr    CopyFile                          (     const FSRef                     *source,
00211                                                                                 FSCatalogInfo           *sourceCatInfo,
00212                                                                                 const FSRef                     *destDir,
00213                                                                                 const HFSUniStr255      *destName,                      /* can be NULL */
00214                                                                                 CopyParams                      *copyParams,
00215                                                                                 FSRef                           *newRef,                        /* can be NULL */
00216                                                                                 FSSpec                          *newSpec );                     /* can be NULL */
00217                                                                 
00218 static  OSErr   FSUsePBHCopyFile          (     const FSRef                     *srcFileRef,
00219                                                                                 const FSRef             *dstDirectoryRef,
00220                                                                                 const HFSUniStr255      *destName,                      /* can be NULL (no rename during copy) */
00221                                                                                 TextEncoding            textEncodingHint,
00222                                                                                 FSRef                           *newRef,                        /* can be NULL */
00223                                                                                 FSSpec                          *newSpec );                     /* can be NULL */
00224                                                                                         
00225 static OSErr    DoCopyFile                        (     const FSRef             *source,
00226                                                                                 FSCatalogInfo           *sourceCatInfo,
00227                                                                                 const FSRef                     *destDir,
00228                                                                                 const HFSUniStr255      *destName,
00229                                                                                 CopyParams                      *params, 
00230                                                                                 FSRef                           *newRef,                        /* can be NULL */
00231                                                                                 FSSpec                          *newSpec );                     /* can be NULL */
00232 
00233 static OSErr    FSCopyFolder              (     const FSRef                     *source,
00234                                                                                 const FSRef                     *destDir,
00235                                                                                 const FSCatalogInfo     *sourceCatInfo,
00236                                                                                 const HFSUniStr255      *newFoldName,
00237                                                                                 CopyParams                      *copyParams,
00238                                                                                 FilterParams            *filterParams,
00239                                                                                 ItemCount                        maxLevels,
00240                                                                                 FSRef                           *outDirRef,                     /* can be NULL */
00241                                                                                 FSSpec                          *outDirSpec );          /* can be NULL */
00242 
00243 static OSErr    ProcessFolderList         (     FSCopyFolderGlobals *folderGlobals );
00244 
00245 static OSErr    CopyFolder                        (     FSCopyFolderGlobals     *folderGlobals );
00246 
00247 static OSErr    CheckForDestInsideSrc ( const FSRef                     *source,
00248                                                                                 const FSRef                     *destDir );
00249 
00250 static OSErr    CopyForks                         (     const FSRef                     *source,
00251                                                                                 const FSRef                     *dest,
00252                                                                                 CopyParams                      *params );
00253 
00254 static OSErr    CopyForksToDisk           (     const FSRef                     *source,
00255                                                                                 const FSRef                     *dest,
00256                                                                                 CopyParams                      *params );
00257                                                                                 
00258 static OSErr    CopyForksToDropBox        (     const FSRef                     *source,
00259                                                                                 const FSRef                     *dest,
00260                                                                                 CopyParams                      *params );
00261 
00262 static OSErr    OpenAllForks              (     const FSRef                     *dest,
00263                                                                                 GenLinkedList           *forkList );
00264 
00265 static OSErr    WriteFork                         (     const SInt16            srcRefNum,
00266                                                                                 const SInt16            destRefNum,
00267                                                                                 const CopyParams        *params,
00268                                                                                 const SInt64            forkSize );
00269 
00270 static UInt32   CalcBufferSizeForVol  ( const GetVolParmsInfoBuffer *volParms,
00271                                                                                 UInt32                          volParmsSize );
00272 
00273 static UInt32   BufferSizeForVolSpeed ( UInt32                          volumeBytesPerSecond );
00274 
00275 static OSErr    FSDeleteFolder            (     const FSRef                     *container );
00276 
00277 static void             FSDeleteFolderLevel       (     const FSRef                     *container,
00278                                                                                 FSDeleteObjectGlobals *theGlobals );
00279 
00280 static OSErr    IsDropBox                         (     const FSRef                     *source,
00281                                                                                 Boolean                         *isDropBox );
00282 
00283 static OSErr    GetMagicBusyCreateDate( UTCDateTime                     *date );
00284 
00285 static OSErr    FSGetVRefNum              (     const FSRef                     *ref,
00286                                                                                 FSVolumeRefNum          *vRefNum );
00287 
00288 static OSErr    FSGetVolParms             (     FSVolumeRefNum            volRefNum,
00289                                                                                 UInt32                            bufferSize,
00290                                                                                 GetVolParmsInfoBuffer*volParmsInfo,
00291                                                                                 UInt32                           *actualInfoSize );     /*      Can Be NULL     */
00292 
00293 static OSErr    UniStrToPStr              (     const HFSUniStr255      *uniStr,
00294                                                                                 TextEncoding             textEncodingHint,
00295                                                                                 Boolean                          isVolumeName,
00296                                                                                 Str255                           pStr );
00297 
00298 static OSErr    FSMakeFSRef                       (     FSVolumeRefNum           volRefNum,
00299                                                                                 SInt32                           dirID,
00300                                                                                 ConstStr255Param         name,
00301                                                                                 FSRef                           *ref );
00302                                                 
00303 static OSErr    SetupDestination          (     const FSRef                     *destDir,
00304                                                                                 const DupeAction         dupeAction,
00305                                                                                 HFSUniStr255            *sourceName,
00306                                                                                 FSRef                           *deleteMeRef,
00307                                                                                 Boolean                         *isReplacing);
00308 
00309 static OSErr    GetUniqueName             (     const FSRef                     *destDir,
00310                                                                                 HFSUniStr255            *sourceName );
00311 
00312 static OSErr    GetObjectName             (     const FSRef                     *sourceRef,
00313                                                                                 HFSUniStr255            *sourceName,
00314                                                                                 TextEncoding            *sourceEncoding );
00315                                                                         
00316 static OSErr    CreateFolder              (     const FSRef                     *sourceRef,
00317                                                                                 const FSRef                     *destDirRef,
00318                                                                                 const FSCatalogInfo     *catalogInfo,
00319                                                                                 const HFSUniStr255      *folderName,
00320                                                                                 CopyParams                      *params,
00321                                                                                 FSRef                           *newFSRefPtr,
00322                                                                                 FSSpec                          *newFSSpecPtr );
00323 
00324 static OSErr    DoCreateFolder            (     const FSRef                     *sourceRef,
00325                                                                                 const FSRef                     *destDirRef,
00326                                                                                 const FSCatalogInfo     *catalogInfo,
00327                                                                                 const HFSUniStr255      *folderName,
00328                                                                                 CopyParams                      *params,
00329                                                                                 FSRef                           *newFSRefPtr,
00330                                                                                 FSSpec                          *newFSSpecPtr);
00331 
00332 static pascal void MyDisposeDataProc  ( void                            *pData );
00333 
00334 static pascal void MyCloseForkProc        (     void                            *pData );
00335 
00336 /*****************************************************************************/
00337 /*****************************************************************************/
00338 /*****************************************************************************/
00339 
00340 #pragma mark ----- Copy Objects -----
00341 
00342         /* This routine acts as the top level of the copy engine.       */ 
00343 OSErr FSCopyObject(     const FSRef                             *source,
00344                                         const FSRef                             *destDir,
00345                                         ItemCount                               maxLevels,
00346                                         FSCatalogInfoBitmap             whichInfo,
00347                                         DupeAction                              dupeAction,
00348                                         const HFSUniStr255              *newObjectName, /* can be NULL */
00349                                         Boolean                                 wantFSSpec,
00350                                         Boolean                                 wantName,
00351                                         CopyObjectFilterProcPtr filterProcPtr,  /* can be NULL */
00352                                         void                                    *yourDataPtr,   /* can be NULL */
00353                                         FSRef                                   *newObjectRef,  /* can be NULL */
00354                                         FSSpec                                  *newObjectSpec) /* can be NULL */
00355 {
00356         CopyParams      copyParams;
00357         FilterParams    filterParams;
00358         FSCatalogInfo   sourceCatInfo;
00359         HFSUniStr255    sourceName,
00360                                         tmpObjectName;
00361         FSRef                   tmpObjectRef,
00362                                         deleteMeRef;
00363         Boolean                 isDirectory = false,
00364                                         isReplacing = false;
00365         OSErr                   err = ( source != NULL && destDir != NULL ) ? noErr : paramErr;
00366 
00367                 /* Zero out these two FSRefs in case an error occurs before or  */
00368                 /* inside FSCopyObjectPreflight.  Paranoia mainly...                    */
00369         BlockZero( &deleteMeRef,        sizeof( FSRef ) );
00370         BlockZero( &tmpObjectRef,       sizeof( FSRef ) );
00371 
00372                 /* setup filterParams */
00373         filterParams.whichInfo          = whichInfo;
00374         filterParams.filterProcPtr      = filterProcPtr;
00375         filterParams.wantSpec           = ( filterProcPtr && wantFSSpec );      /* only get this info if        */
00376         filterParams.wantName           = ( filterProcPtr && wantName );        /* a filterProc is provied      */
00377         filterParams.yourDataPtr        = yourDataPtr;
00378         
00379                 /* Get and store away the name of the source object */
00380                 /* and setup the initial name of the new object         */
00381         if( err == noErr )
00382                 err = GetObjectName( source, &sourceName, NULL );
00383         if( err == noErr )
00384                 tmpObjectName = (newObjectName != NULL) ? *newObjectName : sourceName;
00385 
00386         if( err == noErr )              /* preflight/prep the destination and our internal variables */
00387                 err = FSCopyObjectPreflight( source, destDir, dupeAction, &sourceCatInfo, &copyParams, &tmpObjectName, &deleteMeRef, &isReplacing, &isDirectory );
00388                 
00389                                                         /* now that we have some info, lets print it */
00390         if( err == noErr )
00391         {
00392                 dwarning(( "%s -- err: %d, maxLevels: %u, whichInfo: %08x,\n", __FUNCTION__, err, (unsigned int)maxLevels, (int)whichInfo ));
00393                 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" ));
00394                 dwarning(( "\t\t\t\tfilterProcPtr: 0x%08x, yourDataPtr: 0x%08x,\n", (unsigned int)filterProcPtr, (unsigned int)yourDataPtr ));
00395                 dwarning(( "\t\t\t\tnewObjectRef: 0x%08x, newObjectSpec: 0x%08x,\n", (unsigned int)newObjectRef, (unsigned int)newObjectSpec ));
00396                 dwarning(( "\t\t\t\tcopyBufferSize: %dkB, isDirectory: %s, isLocal: %s,\n", (int)copyParams.copyBufferSize/1024, (isDirectory)?"yes":"no", (copyParams.copyingToLocalVolume)?"yes":"no" ));
00397                 dwarning(( "\t\t\t\tisDropBox: %s, PBHCopyFileSync supported: %s\n\n", (copyParams.copyingToDropFolder)?"yes":"no", (copyParams.volHasCopyFile)?"yes":"no" ));
00398         }
00399                 
00400         if( err == noErr )              /* now copy the file/folder... */
00401         {               /* is it a folder? */
00402                 if ( isDirectory )
00403                 {               /* yes */
00404                         err = CheckForDestInsideSrc(source, destDir);                   
00405                         if( err == noErr )
00406                                 err = FSCopyFolder( source, destDir, &sourceCatInfo, &tmpObjectName, &copyParams, &filterParams, maxLevels, &tmpObjectRef, newObjectSpec );
00407                 }
00408                 else    /* no */
00409                         err = FSCopyFile(source, destDir, &sourceCatInfo, &tmpObjectName, &copyParams, &filterParams, &tmpObjectRef, newObjectSpec);
00410         }
00411         
00412                 /* if an object existed in the destination with the same name as        */
00413                 /* the source and the caller wants to replace it, we had renamed it     */
00414                 /* to ".DeleteMe" earlier.  If no errors, we delete it, else delete     */
00415                 /* the one we just created and rename the origenal back to its          */
00416                 /* origenal name.                                                                                                       */
00417                 /*                                                                                                                                      */
00418                 /* This is done mainly to cover the case of the source being in the     */
00419                 /* destination directory when kDupeActionReplace is selected            */
00420                 /* (3188701)                                                                                                            */
00421         if( copyParams.dupeAction == kDupeActionReplace && isReplacing == true )
00422         {
00423                 dwarning(("%s -- Cleaning up, this might take a moment.  err : %d\n", __FUNCTION__, err));
00424         
00425                 if( err == noErr )
00426                         err = FSDeleteObjects( &deleteMeRef );
00427                 else    
00428                 {               /* not much we can do if the delete or rename fails, we need to preserve        */
00429                                 /* the origenal error code that got us here.                                                            */
00430                                 /*                                                                                                                                                      */
00431                                 /* If an error occurs before or inside SetupDestination, newFileRef and         */
00432                                 /* deleteMeRef will be invalid so the delete and rename will simply fail        */
00433                                 /* leaving the source and destination unchanged                                                         */
00434                         myverify_noerr( FSDeleteObjects( &tmpObjectRef ) );
00435                         myverify_noerr( FSRenameUnicode( &deleteMeRef, sourceName.length, sourceName.unicode, sourceCatInfo.textEncodingHint, NULL ) );
00436                 }
00437         }
00438         
00439         if( err == noErr && newObjectRef != NULL )
00440                 *newObjectRef = tmpObjectRef;
00441 
00442                 /* Clean up for space and safety...  Who me? */
00443         if( copyParams.copyBuffer != NULL )
00444                 DisposePtr((char*)copyParams.copyBuffer);
00445                 
00446         mycheck_noerr( err );   
00447         
00448         return err;
00449 }                                
00450 
00451 /*****************************************************************************/
00452 
00453         /* Does a little preflighting (as the name suggests) to figure out the optimal  */
00454         /* buffer size, if its a drop box, on a remote volume etc                                               */
00455 static OSErr FSCopyObjectPreflight(     const FSRef                     *source,
00456                                                                         const FSRef                     *destDir,
00457                                                                         const DupeAction        dupeAction,
00458                                                                         FSCatalogInfo           *sourceCatInfo,
00459                                                                         CopyParams              *copyParams,
00460                                                                         HFSUniStr255            *newObjectName,
00461                                                                         FSRef                           *deleteMeRef,
00462                                                                         Boolean                         *isReplacing,
00463                                                                         Boolean                         *isDirectory)
00464 {
00465         GetVolParmsInfoBuffer   srcVolParms,
00466                                                         destVolParms;
00467         UInt32                                  srcVolParmsSize = 0,
00468                                                         destVolParmsSize = 0;
00469         FSVolumeRefNum                  srcVRefNum = 0,
00470                                                         destVRefNum = 0;
00471         OSErr                                   err = ( source            != NULL && destDir     != NULL &&
00472                                                                         sourceCatInfo != NULL && copyParams      != NULL &&
00473                                                                         newObjectName != NULL && deleteMeRef != NULL &&
00474                                                                         isDirectory       != NULL ) ? noErr : paramErr;
00475 
00476         BlockZero( copyParams, sizeof( CopyParams ) );
00477 
00478         copyParams->dupeAction = dupeAction;
00479 
00480         if( err == noErr )              /* Get the info we will need later about the source object      */
00481                 err = FSGetCatalogInfo( source, kFSCatInfoSettableInfo, sourceCatInfo, NULL, NULL, NULL );              
00482         if( err == noErr )              /* get the source's vRefNum                                                                     */
00483                 err = FSGetVRefNum( source, &srcVRefNum );
00484         if( err == noErr )              /* get the source's volParams                                                           */
00485                 err = FSGetVolParms( srcVRefNum,  sizeof(GetVolParmsInfoBuffer), &srcVolParms, &srcVolParmsSize );                      
00486         if( err == noErr )              /* get the destination's vRefNum                                                        */
00487                 err = FSGetVRefNum( destDir, &destVRefNum );
00488         if( err == noErr )
00489         {
00490                                                         /* Calculate the optimal copy buffer size for the src vol       */
00491                 copyParams->copyBufferSize = CalcBufferSizeForVol( &srcVolParms, srcVolParmsSize );
00492         
00493                                                         /* if src and dest on different volumes, get its vol parms      */
00494                                                         /* and calculate its optimal buffer size                                        */
00495                                                         /* else destVolParms = srcVolParms                                                      */
00496                 if( srcVRefNum != destVRefNum )
00497                 {
00498                         err = FSGetVolParms( destVRefNum, sizeof(GetVolParmsInfoBuffer), &destVolParms, &destVolParmsSize );
00499                         if( err == noErr )
00500                         {
00501                                 ByteCount tmpBufferSize = CalcBufferSizeForVol( &destVolParms, destVolParmsSize );
00502                                 if( tmpBufferSize < copyParams->copyBufferSize )
00503                                         copyParams->copyBufferSize = tmpBufferSize;                             
00504                         }
00505                 }
00506                 else 
00507                         destVolParms = srcVolParms;
00508         }
00509         if( err == noErr )
00510                 err = ((copyParams->copyBuffer = NewPtr( copyParams->copyBufferSize )) != NULL ) ? noErr : MemError();
00511 
00512                 /* figure out if source is a file or folder                     */
00513                 /*                        if it is on a local volume,                   */
00514                 /*                        if destination is a drop box                  */
00515                 /*                        if source and dest are on same server */
00516                 /*                        and if it supports PBHCopyFile                */
00517         if( err == noErr )              /* is the destination a Drop Box        */
00518                 err = IsDropBox( destDir, &copyParams->copyingToDropFolder );
00519         if( err == noErr )
00520         {
00521                         /* Is it a directory                                                                    */
00522                 *isDirectory = ((sourceCatInfo->nodeFlags & kFSNodeIsDirectoryMask) != 0);
00523                         /* destVolParms.vMServerAdr is non-zero for remote volumes      */
00524                 copyParams->copyingToLocalVolume = (destVolParms.vMServerAdr == 0);
00525                 if( !copyParams->copyingToLocalVolume )
00526                 {
00527                                 /* If the destination is on a remote volume, and source and dest are on         */
00528                                 /* the same server, then it might support PBHCopyFileSync                                       */
00529                                 /* If not, then PBHCopyFileSync won't work                                                                      */
00530 
00531                                 /* figure out if the volumes support PBHCopyFileSync                                            */
00532                         copyParams->volHasCopyFile = ( err == noErr && destVolParms.vMServerAdr == srcVolParms.vMServerAdr ) ?
00533                                                                                    VolHasCopyFile(&srcVolParms) : false;
00534                 }       
00535         }
00536         
00537         if( err == noErr )
00538                 err = SetupDestination( destDir, copyParams->dupeAction, newObjectName, deleteMeRef, isReplacing );
00539                 
00540         return err;
00541 }
00542 
00543 #pragma mark ----- Copy Files -----
00544 
00545 /*****************************************************************************/
00546 
00547 static OSErr FSCopyFile(        const FSRef                     *source,
00548                                                         const FSRef                     *destDir,
00549                                                         const FSCatalogInfo     *sourceCatInfo,
00550                                                         const HFSUniStr255      *newFileName,
00551                                                         CopyParams                      *copyParams,
00552                                                         FilterParams            *filterParams,
00553                                                         FSRef                           *outFileRef,
00554                                                         FSSpec                          *outFileSpec )
00555 {
00556         FSCatalogInfo   catInfo = *sourceCatInfo;
00557         FSRef                   newFileRef;
00558         FSSpec                  newFileSpec;
00559         OSErr                   err = ( source != NULL && destDir != NULL &&
00560                                                         copyParams != NULL && filterParams != NULL ) ? noErr : paramErr;
00561         
00562                                                         /* If you would like a Pre-Copy filter (i.e to weed out objects */
00563                                                         /* you don't want to copy) you should add it here                               */
00564         
00565         if( err == noErr )              /* copy the file over */
00566                 err = CopyFile( source, &catInfo, destDir, newFileName, copyParams, &newFileRef, (filterParams->wantSpec || outFileSpec) ? &newFileSpec : NULL );
00567 
00568                 /* Call the IterateFilterProc _after_ the new file was created even if an error occured.        */
00569                 /* Note: if an error occured above, the FSRef and other info might not be valid                         */
00570         if( filterParams->filterProcPtr != NULL )
00571         {
00572                         /* get the extra info the user wanted on the new file that we don't have */
00573                 if( err == noErr && (filterParams->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone )
00574                         err = FSGetCatalogInfo( &newFileRef, filterParams->whichInfo & ~kFSCatInfoSettableInfo, &catInfo, NULL, NULL, NULL );   
00575 
00576                 err = CallCopyObjectFilterProc( filterParams->filterProcPtr, false, 0, err, &catInfo, &newFileRef, 
00577                                                                                 (filterParams->wantSpec) ? &newFileSpec : NULL,
00578                                                                                 (filterParams->wantName) ? newFileName : NULL,
00579                                                                                 filterParams->yourDataPtr);
00580         }
00581         
00582         if( err == noErr )
00583         {       
00584                 if( outFileRef != NULL )
00585                         *outFileRef             = newFileRef;
00586                 if( outFileSpec != NULL )
00587                         *outFileSpec    = newFileSpec;
00588         }
00589                 
00590         mycheck_noerr(err);
00591 
00592         return err;
00593 }
00594 
00595 /*****************************************************************************/
00596 
00597 static OSErr CopyFile(  const FSRef                     *source,
00598                                                 FSCatalogInfo           *sourceCatInfo,
00599                                                 const FSRef                     *destDir,
00600                                                 const HFSUniStr255      *destName,              /* can be NULL */
00601                                                 CopyParams                      *params,
00602                                                 FSRef                           *newFile,               /* can be NULL */
00603                                                 FSSpec                          *newSpec )              /* can be NULL */
00604 {
00605         OSErr           err = paramErr;
00606         
00607                 /* Clear the "inited" bit so that the Finder positions the icon for us. */
00608         ((FInfo *)(sourceCatInfo->finderInfo))->fdFlags &= ~kHasBeenInited;
00609 
00610                 /* if the volumes support PBHCopyFileSync, try to use it                        */
00611         if( params->volHasCopyFile == true )
00612                 err = FSUsePBHCopyFile( source, destDir, destName, kTextEncodingUnknown, newFile, newSpec );
00613                         
00614                                                         /* if PBHCopyFile didn't work or not supported, */
00615         if( err != noErr )              /* then try old school file transfer                    */
00616                 err = DoCopyFile( source, sourceCatInfo, destDir, destName, params, newFile, newSpec );         
00617 
00618         mycheck_noerr(err);
00619 
00620         return err;
00621 }
00622 
00623 /*****************************************************************************/
00624 
00625         /* Wrapper function for PBHCopyFileSync */
00626 static OSErr FSUsePBHCopyFile(  const FSRef                     *srcFileRef,
00627                                                                 const FSRef                     *dstDirectoryRef,
00628                                                                 const HFSUniStr255      *destName,                      /* can be NULL */
00629                                                                 TextEncoding            textEncodingHint,
00630                                                                 FSRef                           *newRef,                        /* can be NULL */
00631                                                                 FSSpec                          *newSpec)                       /* can be NULL */
00632 {
00633         FSSpec                                  srcFileSpec;
00634         FSCatalogInfo                   catalogInfo;
00635         HParamBlockRec                  pb;
00636         Str255                                  hfsName;
00637         OSErr                                   err = ( srcFileRef != NULL && dstDirectoryRef != NULL ) ? noErr : paramErr;
00638         
00639         if( err == noErr )              /* get FSSpec of source FSRef */
00640                 err = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL);
00641         if( err == noErr )              /* get the destination vRefNum and nodeID (nodeID is the dirID) */
00642                 err = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume | kFSCatInfoNodeID, &catalogInfo, NULL, NULL, NULL);
00643         if( err == noErr )              /* gather all the info needed */
00644         {
00645                 pb.copyParam.ioVRefNum          = srcFileSpec.vRefNum;
00646                 pb.copyParam.ioDirID            = srcFileSpec.parID;
00647                 pb.copyParam.ioNamePtr          = (StringPtr)srcFileSpec.name;
00648                 pb.copyParam.ioDstVRefNum       = catalogInfo.volume;
00649                 pb.copyParam.ioNewDirID         = (long)catalogInfo.nodeID;
00650                 pb.copyParam.ioNewName          = NULL;
00651                 if( destName != NULL )
00652                         err = UniStrToPStr( destName, textEncodingHint, false, hfsName );
00653                 pb.copyParam.ioCopyName         = ( destName != NULL && err == noErr ) ? hfsName : NULL;
00654         }
00655         if( err == noErr )                      /* tell the server to copy the object */
00656                 err = PBHCopyFileSync(&pb);
00657         
00658         if( err == noErr )
00659         {
00660                 if( newSpec != NULL )   /* caller wants an FSSpec, so make it */                
00661                         myverify_noerr(FSMakeFSSpec( pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, pb.copyParam.ioCopyName, newSpec));
00662                 if( newRef != NULL )    /* caller wants an FSRef, so make it */
00663                         myverify_noerr(FSMakeFSRef( pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, pb.copyParam.ioCopyName, newRef));
00664         }
00665         
00666         if( err != paramErr )           /* returning paramErr is ok, it means PBHCopyFileSync was not supported */
00667                 mycheck_noerr(err);
00668 
00669         return err;
00670 }
00671 
00672 /*****************************************************************************/
00673 
00674         /* Copies a file referenced by source to the directory referenced by    */
00675         /* destDir.  destName is the name the file we are going to copy to the  */
00676         /* destination.  sourceCatInfo is the catalog info of the file, which   */
00677         /* is passed in as an optimization (we could get it by doing a                  */
00678         /* FSGetCatalogInfo but the caller has already done that so we might as */
00679         /* well take advantage of that).                                                                                */
00680         /*                                                                                                                                              */
00681 static OSErr DoCopyFile(const FSRef                     *source,
00682                                                 FSCatalogInfo           *sourceCatInfo,
00683                                                 const FSRef                     *destDir,
00684                                                 const HFSUniStr255      *destName,
00685                                                 CopyParams                      *params,
00686                                                 FSRef                           *newRef,
00687                                                 FSSpec                          *newSpec )
00688 {
00689         FSRef                           dest;
00690         FSSpec                          tmpSpec;
00691         FSPermissionInfo        originalPermissions;
00692         OSType                          originalFileType = 'xxxx';
00693         UInt16                          originalNodeFlags = kFSCatInfoNone;
00694         Boolean                         getSpec;
00695         OSErr                           err = noErr;
00696 
00697                 /* If we're copying to a drop folder, we won't be able to reset this            */
00698                 /* information once the copy is done, so we don't mess it up in                         */
00699                 /* the first place.  We still clear the locked bit though; items dropped        */
00700                 /* into a drop folder always become unlocked.                                                           */
00701         if (!params->copyingToDropFolder)
00702         {
00703                         /* Remember to clear the file's type, so the Finder doesn't                             */
00704                         /* look at the file until we're done.                                                                   */
00705                 originalFileType = ((FInfo *) &sourceCatInfo->finderInfo)->fdType;
00706                 ((FInfo *) &sourceCatInfo->finderInfo)->fdType = kFirstMagicBusyFiletype;
00707 
00708                         /* Remember and clear the file's locked status, so that we can                  */
00709                         /* actually write the forks we're about to create.                                              */
00710                 originalNodeFlags = sourceCatInfo->nodeFlags;
00711         }
00712         sourceCatInfo->nodeFlags &= ~kFSNodeLockedMask;
00713         
00714                 /* figure out if we should get the FSSpec to the new file or not                        */
00715                 /* If the caller asked for it, or if we need it for symlinks                            */
00716         getSpec = ( ( newSpec != NULL ) || ( !params->copyingToDropFolder && originalFileType == 'slnk' && ((FInfo *) &sourceCatInfo->finderInfo)->fdCreator == 'rhap' ) );
00717         
00718                 /* we need to have user level read/write/execute access to the file we are      */
00719                 /* going to create otherwise FSCreateFileUnicode will return                            */
00720                 /* -5000 (afpAccessDenied), and the FSRef returned will be invalid, yet         */
00721                 /* the file is created (size 0k)... bug?                                                                        */
00722         originalPermissions = *((FSPermissionInfo*)sourceCatInfo->permissions);
00723         ((FSPermissionInfo*)sourceCatInfo->permissions)->mode |= kRWXUserAccessMask;
00724         
00725                 /* Classic only supports 9.1 and higher, so we don't have to worry                      */
00726                 /* about 2397324                                                                                                                        */
00727         if( err == noErr )
00728                 err = FSCreateFileUnicode(destDir, destName->length, destName->unicode, kFSCatInfoSettableInfo, sourceCatInfo, &dest, ( getSpec ) ? &tmpSpec : NULL );
00729         if( err == noErr )      /* Copy the forks over to the new file                                          */
00730                 err = CopyForks(source, &dest, params);
00731 
00732                 /* Restore the original file type, creation and modification dates,                     */
00733                 /* locked status and permissions.                                                                                       */
00734                 /* This is one of the places where we need to handle drop                                       */
00735                 /* folders as a special case because this FSSetCatalogInfo will fail for        */
00736                 /* an item in a drop folder, so we don't even attempt it.                                       */
00737         if (err == noErr && !params->copyingToDropFolder)
00738         {
00739                 ((FInfo *) &sourceCatInfo->finderInfo)->fdType = originalFileType;
00740                 sourceCatInfo->nodeFlags  = originalNodeFlags;
00741                 *((FSPermissionInfo*)sourceCatInfo->permissions) = originalPermissions;
00742 
00743                         /* 2796751, FSSetCatalogInfo returns -36 when setting the Finder Info   */
00744                         /* for a symlink.  To workaround this, when the file is a                               */
00745                         /* symlink (slnk/rhap) we will finish the copy in two steps. First              */
00746                         /* setting everything but the Finder Info on the file, then calling             */
00747                         /* FSpSetFInfo to set the Finder Info for the file. I would rather use  */
00748                         /* an FSRef function to set the Finder Info, but FSSetCatalogInfo is    */
00749                         /* the only one...  catch-22...                                                                                 */
00750                         /*                                                                                                                                              */
00751                         /* The Carbon File Manager always sets the type/creator of a symlink to */
00752                         /* slnk/rhap if the file is a symlink we do the two step, if it isn't   */
00753                         /* we use FSSetCatalogInfo to do all the work.                                                  */
00754                 if ((originalFileType == 'slnk') && (((FInfo *) &sourceCatInfo->finderInfo)->fdCreator == 'rhap'))
00755                 {                                                               /* Its a symlink                                                        */
00756                                                                                 /* set all the info, except the Finder info     */
00757                         err = FSSetCatalogInfo(&dest, kFSCatInfoNodeFlags | kFSCatInfoPermissions, sourceCatInfo);
00758                         if ( err == noErr )                     /* set the Finder Info to that file                     */
00759                                 err = FSpSetFInfo( &tmpSpec, ((FInfo *) &sourceCatInfo->finderInfo) );
00760                 }
00761                 else                                                    /* its a regular file                                           */
00762                         err = FSSetCatalogInfo(&dest, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo | kFSCatInfoPermissions, sourceCatInfo);
00763         }
00764         
00765                 /* If we created the file and the copy failed, try to clean up by                       */
00766                 /* deleting the file we created.  We do this because, while it's                        */
00767                 /* possible for the copy to fail halfway through and the File Manager           */
00768                 /* doesn't really clean up that well in that case, we *really* don't want       */
00769                 /* any half-created files being left around.                                                            */
00770                 /* if the file already existed, we don't want to delete it                                      */
00771         if( err == noErr || err == dupFNErr )
00772         {                       /* if everything was fine, then return the new file Spec/Ref            */
00773                 if( newRef != NULL )
00774                         *newRef = dest;
00775                 if( newSpec != NULL )
00776                         *newSpec = tmpSpec;
00777         }
00778         else    
00779                 myverify_noerr( FSDeleteObjects(&dest) );
00780 
00781         mycheck_noerr(err);
00782 
00783         return err;
00784 }
00785 
00786 /*****************************************************************************/
00787 
00788 #pragma mark ----- Copy Folders -----
00789 
00790 static OSErr FSCopyFolder(      const FSRef                     *source,
00791                                                         const FSRef                     *destDir,
00792                                                         const FSCatalogInfo     *sourceCatInfo,
00793                                                         const HFSUniStr255      *newObjectName,
00794                                                         CopyParams                      *copyParams, 
00795                                                         FilterParams            *filterParams,
00796                                                         ItemCount                       maxLevels,
00797                                                         FSRef                           *outDirRef,
00798                                                         FSSpec                          *outDirSpec )
00799 {
00800         FSCopyFolderGlobals     folderGlobals;
00801         FolderListData          *tmpListData            = NULL;
00802         FSCatalogInfo           catInfo = *sourceCatInfo;
00803         FSRef                           newDirRef;
00804         FSSpec                          newDirSpec;
00805         OSErr                           err;
00806 
00807                                                         /* setup folder globals */
00808         folderGlobals.catInfoList               = (FSCatalogInfo*)      NewPtr( sizeof( FSCatalogInfo ) * kNumObjects );
00809         folderGlobals.srcRefList                = (FSRef*)                      NewPtr( sizeof( FSRef )                 * kNumObjects );
00810         folderGlobals.nameList                  = (HFSUniStr255*)       NewPtr( sizeof( HFSUniStr255 )  * kNumObjects );
00811         folderGlobals.folderListIter    = NULL;
00812         folderGlobals.copyParams                = copyParams;
00813         folderGlobals.filterParams              = filterParams;
00814         folderGlobals.maxLevels                 = maxLevels;
00815         folderGlobals.currentLevel              = 0;
00816 
00817                                                         /* if any of the NewPtr calls failed, we MUST bail */
00818         err                                                             = ( folderGlobals.catInfoList   != NULL &&
00819                                                                                 folderGlobals.srcRefList        != NULL &&
00820                                                                                 folderGlobals.nameList          != NULL ) ? noErr : memFullErr;
00821 
00822                                                         /* init the linked list we will use to keep track of the folders */
00823         InitLinkedList( &folderGlobals.folderList, MyDisposeDataProc );
00824 
00825         if( err == noErr && !copyParams->copyingToDropFolder )
00826                 err = GetMagicBusyCreateDate( &catInfo.createDate );
00827         if( err == noErr )              /* create the directory */
00828                 err = DoCreateFolder( source, destDir, &catInfo, newObjectName, folderGlobals.copyParams, &newDirRef, (filterParams->wantSpec || outDirSpec ) ? &newDirSpec : NULL );
00829         
00830                 /* Note: if an error occured above, the FSRef and other info might not be valid */
00831         if( filterParams->filterProcPtr != NULL )
00832         {
00833                         /* get the info the user wanted about the source directory we don't have */
00834                 if( err == noErr && (filterParams->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone )
00835                         err = FSGetCatalogInfo(&newDirRef, filterParams->whichInfo & ~kFSCatInfoSettableInfo, &catInfo, NULL, NULL, NULL);
00836 
00837                 err = CallCopyObjectFilterProc(filterParams->filterProcPtr, false, folderGlobals.currentLevel,
00838                                                                            err, &catInfo, &newDirRef,
00839                                                                            ( filterParams->wantSpec ) ? &newDirSpec : NULL,
00840                                                                            ( filterParams->wantName ) ? newObjectName : NULL,
00841                                                                              filterParams->yourDataPtr);
00842         }
00843         if( err == noErr )              /* create the memory for this folder */
00844                 err = ( ( tmpListData = (FolderListData*) NewPtr( sizeof( FolderListData ) ) ) != NULL ) ? noErr : MemError();
00845         if( err == noErr )
00846         {               /* setup the folder info */
00847                 tmpListData->sourceDirRef       = *source;
00848                 tmpListData->destDirRef         = newDirRef;
00849                 tmpListData->level                      = folderGlobals.currentLevel;
00850                         /* add this folder to the list to give ProcessFolderList something to chew on */
00851                 err = AddToTail( &folderGlobals.folderList, tmpListData );
00852                 if( err == noErr )                      /* tmpListData added successfully       */
00853                         err = ProcessFolderList( &folderGlobals );
00854                 else                                            /* error occured, so dispose of memory */
00855                         DisposePtr( (char*) tmpListData );
00856         }
00857         
00858         dwarning(("\n%s -- %u folders were found\n", __FUNCTION__, (unsigned int)GetNumberOfItems( &folderGlobals.folderList ) ));
00859         
00860                 /* when we're done destroy the list and free up any memory we allocated */
00861         DestroyList( &folderGlobals.folderList );
00862 
00863                 /* now that the copy is complete, we can set things back to normal      */
00864                 /* for the directory we just created.                                                           */
00865                 /* We have to do this only for the top directory of the copy            */
00866                 /* all subdirectories were created all at once                                          */
00867         if( err == noErr && !folderGlobals.copyParams->copyingToDropFolder )
00868                 err = FSSetCatalogInfo( &newDirRef, kFSCatInfoCreateDate | kFSCatInfoPermissions, sourceCatInfo );
00869                                         
00870                 /* Copy went as planned, and caller wants an FSRef/FSSpec to the new directory */               
00871         if( err == noErr )
00872         {
00873                 if( outDirRef != NULL)
00874                         *outDirRef = newDirRef;
00875                 if( outDirSpec != NULL )
00876                         *outDirSpec = newDirSpec;
00877         }
00878 
00879                 /* clean up for space and safety, who me? */
00880         if( folderGlobals.catInfoList )
00881                 DisposePtr( (char*) folderGlobals.catInfoList );
00882         if( folderGlobals.srcRefList )
00883                 DisposePtr( (char*) folderGlobals.srcRefList );
00884         if( folderGlobals.nameList )
00885                 DisposePtr( (char*) folderGlobals.nameList );
00886 
00887         mycheck_noerr(err);     
00888 
00889         return ( err );
00890 }
00891 
00892 /*****************************************************************************/
00893 
00894         /* We now store a list of all the folders/subfolders we encounter in the source */
00895         /* Each node in the list contains an FSRef to the source, an FSRef to the               */
00896         /* mirror folder in the destination, and the level in the source that folder    */
00897         /* is on.  This is done so that we can use FSGetCatalogInfoBulk to its full             */
00898         /* potential (getting items in bulk).  We copy the source one folder at a time. */
00899         /* Copying over the contents of each folder before we continue on to the next   */
00900         /* folder in the list.  This allows us to use the File Manager's own caching    */
00901         /* system to our advantage.                                                                                                             */
00902 static OSErr ProcessFolderList( FSCopyFolderGlobals *folderGlobals )
00903 {
00904         FolderListData          *folderListData;
00905         OSErr                           err = noErr;
00906         
00907                 /* iterate through the list of folders and copy over each one individually      */
00908         for( InitIterator( &folderGlobals->folderList, &folderGlobals->folderListIter ); folderGlobals->folderListIter != NULL && err == noErr; Next( &folderGlobals->folderListIter ) )
00909         {
00910                         /* Get the data for this folder */
00911                 folderListData = (FolderListData*) GetData( folderGlobals->folderListIter );
00912                 if( folderListData != NULL )
00913                 {
00914                         #if DEBUG && !TARGET_API_MAC_OS8
00915                         {
00916                                 char    path[1024];             /* Flawfinder: ignore */
00917                                 myverify_noerr(FSRefMakePath( &(folderListData->sourceDirRef),  (unsigned char*)path, 1024 ));
00918                                 dwarning(("\n\n%s -- Copying contents of\n\t%s\n", __FUNCTION__, path));
00919                                 myverify_noerr(FSRefMakePath( &(folderListData->destDirRef),    (unsigned char*)path, 1024 ));
00920                                 dwarning(("\t\tto\n\t%s\n", path));
00921                         }       
00922                         #endif
00923                         
00924                                 /* stuff the data into our globals */
00925                         folderGlobals->sourceDirRef     = &(folderListData->sourceDirRef);
00926                         folderGlobals->destDirRef       = &(folderListData->destDirRef);
00927                         folderGlobals->currentLevel = folderListData->level;
00928                         
00929                                 /* Copy over this folder and add any subfolders to our list of folders  */
00930                                 /* so they will get processed later                                                                             */
00931                         err = CopyFolder( folderGlobals );
00932                 }
00933         }
00934         
00935         return err;
00936 }
00937 
00938 /*****************************************************************************/
00939 
00940         /* Copy the contents of the source into the destination.  If any subfolders */
00941         /* are found, add them to a local list of folders during the loop stage         */
00942         /* Once the copy is done, insert the local list into the global list right      */
00943         /* after the current position in the list.  This is done so we don't jump       */
00944         /* all over the disk getting the different folders to copy                                      */
00945 static OSErr CopyFolder( FSCopyFolderGlobals *folderGlobals )
00946 {
00947         GenLinkedList   tmpList;
00948         FolderListData  *tmpListData = NULL;
00949         FilterParams    *filterPtr = folderGlobals->filterParams;
00950         FSIterator              iterator;
00951         FSRef                   newRef;
00952         FSSpec                  newSpec;
00953         UInt32                  actualObjects;
00954         OSErr                   err,
00955                                         junkErr;
00956         int                             i;
00957 
00958                 /* Init the local list */
00959         InitLinkedList( &tmpList, MyDisposeDataProc);
00960 
00961         err = FSOpenIterator( folderGlobals->sourceDirRef, kFSIterateFlat, &iterator );
00962         if( err == noErr )
00963         {
00964                 do
00965                 {
00966                                 /* grab a bunch of objects (kNumObjects) from this folder and copy them over */
00967                         err = FSGetCatalogInfoBulk( iterator, kNumObjects, &actualObjects, &filterPtr->containerChanged,
00968                                                                                 kFSCatInfoSettableInfo, folderGlobals->catInfoList, folderGlobals->srcRefList,
00969                                                                                 NULL, folderGlobals->nameList );
00970                         if( ( err == noErr || err == errFSNoMoreItems ) &&
00971                                 ( actualObjects != 0 ) )
00972                         {                       
00973                                 dwarning(("%s -- actualObjects retrieved from FSGetCatalogInfoBulk: %u\n",__FUNCTION__, (unsigned int)actualObjects ));
00974                         
00975                                         /* iterate over the objects actually returned */
00976                                 for( i = 0; i < actualObjects; i++ )
00977                                 {
00978                                                 /* Any errors in here will be passed to the filter proc                         */
00979                                                 /* we don't want an error in here to prematurely cancel the copy        */
00980 
00981                                                 /* If you would like a Pre-Copy filter (i.e to weed out objects         */
00982                                                 /* you don't want to copy) you should add it here                                       */
00983 
00984                                                 /* Is the new object a directory?       */                              
00985                                         if( ( folderGlobals->catInfoList[i].nodeFlags & kFSNodeIsDirectoryMask ) != 0 )
00986                                         {               /* yes */
00987                                                 junkErr = CreateFolder( &folderGlobals->srcRefList[i], folderGlobals->destDirRef,
00988                                                                                                 &folderGlobals->catInfoList[i], &folderGlobals->nameList[i],
00989                                                                                                 folderGlobals->copyParams, &newRef, (filterPtr->wantSpec) ? &newSpec : NULL );
00990                                                         /* If maxLevels is zero, we aren't checking levels                              */
00991                                                         /* If currentLevel+1 < maxLevels, add this folder to the list   */
00992                                                 if( folderGlobals->maxLevels == 0 || (folderGlobals->currentLevel + 1) < folderGlobals->maxLevels )
00993                                                 {
00994                                                         if( junkErr == noErr )          /* Create memory for folder list data   */
00995                                                                 junkErr = ( ( tmpListData = (FolderListData*) NewPtr( sizeof( FolderListData ) ) ) != NULL ) ? noErr : MemError();
00996                                                         if( junkErr == noErr )
00997                                                         {                                                       /* Setup the folder list data                   */
00998                                                                 tmpListData->sourceDirRef       = folderGlobals->srcRefList[i];
00999                                                                 tmpListData->destDirRef         = newRef;
01000                                                                 tmpListData->level                      = folderGlobals->currentLevel + 1;
01001                                                                 
01002                                                                                                                 /* Add it to the local list                             */
01003                                                                 junkErr = AddToTail( &tmpList, tmpListData );
01004                                                         }
01005                                                                 /* If an error occured and memory was created, we need to dispose of it */
01006                                                                 /* since it was not added to the list                                                                   */
01007                                                         if( junkErr != noErr && tmpListData != NULL )
01008                                                                 DisposePtr( (char*) tmpListData );
01009                                                 }
01010                                         }
01011                                         else
01012                                         {               /* no */
01013                                                 junkErr = CopyFile(     &folderGlobals->srcRefList[i], &folderGlobals->catInfoList[i], 
01014                                                                                         folderGlobals->destDirRef, &folderGlobals->nameList[i], 
01015                                                                                         folderGlobals->copyParams, &newRef, ( filterPtr->wantSpec ) ? &newSpec : NULL );
01016                                         }
01017                                         
01018                                                 /* Note: if an error occured above, the FSRef and other info might not be valid */
01019                                         if( filterPtr->filterProcPtr != NULL )
01020                                         {
01021                                                 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 */
01022                                                         junkErr = FSGetCatalogInfo( &newRef, filterPtr->whichInfo & ~kFSCatInfoSettableInfo, &folderGlobals->catInfoList[i], NULL, NULL, NULL );
01023 
01024                                                 err = CallCopyObjectFilterProc( filterPtr->filterProcPtr, filterPtr->containerChanged,
01025                                                                                                                 folderGlobals->currentLevel, junkErr,
01026                                                                                                                 &folderGlobals->catInfoList[i], &newRef,
01027                                                                                                                 ( filterPtr->wantSpec ) ? &newSpec : NULL,
01028                                                                                                                 ( filterPtr->wantName ) ? &folderGlobals->nameList[i] : NULL,
01029                                                                                                                 filterPtr->yourDataPtr);
01030                                         }
01031                                 }
01032                         }
01033                 }while( err == noErr );
01034         
01035                         /* errFSNoMoreItems is OK - it only means we hit the end of this level */
01036                         /* afpAccessDenied is OK too - it only means we cannot see inside the directory */
01037                 if( err == errFSNoMoreItems || err == afpAccessDenied )
01038                         err = noErr;
01039 
01040                         /* Insert the local list of folders from the current folder into our global list.  Even */
01041                         /* if no items were added to the local list (due to error, or empty folder), InsertList */
01042                         /* handles it correctly.  We add the local list even if an error occurred.  It will get */
01043                         /* disposed of when the global list is destroyed.  Doesn't hurt to have a couple extra  */
01044                         /* steps when we're going to bail anyways.                                                                                              */
01045                 InsertList( &folderGlobals->folderList, &tmpList, folderGlobals->folderListIter );      
01046                         
01047                         /* Close the FSIterator (closing an open iterator should never fail) */
01048                 (void) FSCloseIterator(iterator);
01049         }
01050 
01051         mycheck_noerr( err );   
01052         
01053         return err;
01054 }
01055 
01056 /*****************************************************************************/
01057 
01058         /* Determines whether the destination directory is equal to the source  */
01059         /* item, or whether it's nested inside the source item.  Returns a              */
01060         /* errFSDestInsideSource if that's the case.  We do this to prevent             */
01061         /* endless recursion while copying.                                                                             */
01062         /*                                                                                                                                              */
01063 static OSErr CheckForDestInsideSrc(     const FSRef     *source,
01064                                                                         const FSRef     *destDir)
01065 {
01066         FSRef                   thisDir = *destDir;
01067         FSCatalogInfo   thisDirInfo;
01068         Boolean                 done = false;
01069         OSErr                   err;
01070         
01071         do
01072         {
01073                 err = FSCompareFSRefs(source, &thisDir);
01074                 if (err == noErr)
01075                         err = errFSDestInsideSource;
01076                 else if (err == diffVolErr)
01077                 {
01078                         err = noErr;
01079                         done = true;
01080                 } 
01081                 else if (err == errFSRefsDifferent)
01082                 {
01083                         /* This is somewhat tricky.  We can ask for the parent of thisDir       */
01084                         /* by setting the parentRef parameter to FSGetCatalogInfo but, if       */
01085                         /* thisDir is the volume's FSRef, this will give us back junk.          */
01086                         /* So we also ask for the parent's dir ID to be returned in the         */
01087                         /* FSCatalogInfo record, and then check that against the node           */
01088                         /* ID of the root's parent (ie 1).  If we match that, we've made        */
01089                         /* it to the top of the hierarchy without hitting source, so            */
01090                         /* we leave with no error.                                                                                      */
01091                         
01092                         err = FSGetCatalogInfo(&thisDir, kFSCatInfoParentDirID, &thisDirInfo, NULL, NULL, &thisDir);
01093                         if( ( err == noErr ) && ( thisDirInfo.parentDirID == fsRtParID ) )
01094                                 done = true;
01095                 }
01096         } while ( err == noErr && ! done );
01097         
01098         mycheck_noerr( err );   
01099 
01100         return err;
01101 }
01102 
01103 /*****************************************************************************/
01104 
01105 #pragma mark ----- Copy Forks -----
01106 
01107         /* This is where the majority of the work is done.  I special cased             */
01108         /* DropBoxes in order to use FSIterateForks to its full potential for   */
01109         /* the more common case (read/write permissions).  It also simplifies   */
01110         /* the code to have it seperate.                                                                                */
01111 static OSErr CopyForks( const FSRef             *source,
01112                                                 const FSRef             *dest,
01113                                                 CopyParams              *params)
01114 {
01115         OSErr                   err;
01116 
01117         err = ( !params->copyingToDropFolder ) ?        CopyForksToDisk         ( source, dest, params ) :
01118                                                                                                 CopyForksToDropBox      ( source, dest, params );
01119 
01120         mycheck_noerr( err );   
01121 
01122         return err;
01123 }
01124 
01125         /* Open each fork individually and copy them over to the destination                            */
01126 static OSErr CopyForksToDisk(   const FSRef     *source,
01127                                                                 const FSRef     *dest,
01128                                                                 CopyParams      *params )
01129 {
01130         HFSUniStr255    forkName;
01131         CatPositionRec  iterator;
01132         SInt64                  forkSize;
01133         SInt16                  srcRefNum,
01134                                         destRefNum;
01135         OSErr                   err;
01136         
01137                 /* need to initialize the iterator before using it */
01138         iterator.initialize = 0;
01139         
01140         do
01141         {
01142                 err = FSIterateForks( source, &iterator, &forkName, &forkSize, NULL );
01143 
01144                         /* Create the fork.  Note: Data and Resource forks are automatically            */
01145                         /* created when the file is created.  FSCreateFork returns noErr for them       */
01146                         /* We also want to create the fork even if there is no data to preserve         */
01147                         /* empty forks                                                                                                                          */
01148                 if( err == noErr )
01149                         err = FSCreateFork( dest, forkName.length, forkName.unicode );
01150 
01151                         /* Mac OS 9.0 has a bug (in the AppleShare external file system,                        */
01152                         /* I think) [2410374] that causes FSCreateFork to return an errFSForkExists     */
01153                         /* error even though the fork is empty.  The following code swallows            */
01154                         /* the error (which is harmless) in that case.                                                          */
01155                 if( err == errFSForkExists && !params->copyingToLocalVolume )
01156                         err = noErr;
01157 
01158                         /* The remainder of this code only applies if there is actual data                      */
01159                         /* in the source fork.                                                                                                          */
01160 
01161                 if( err == noErr && forkSize > 0 )
01162                 {
01163                         destRefNum = srcRefNum = 0;
01164                         
01165                                                                         /* Open the destination fork    */
01166                         err = FSOpenFork(dest, forkName.length, forkName.unicode, fsWrPerm, &destRefNum);
01167                         if( err == noErr )              /* Open the source fork                 */
01168                                 err = FSOpenFork(source, forkName.length, forkName.unicode, fsRdPerm, &srcRefNum);
01169                         if( err == noErr )              /* Write the fork to disk               */
01170                                 err = WriteFork( srcRefNum, destRefNum, params, forkSize );
01171 
01172                         if( destRefNum  != 0 )  /* Close the destination fork   */
01173                                 myverify_noerr( FSCloseFork( destRefNum ) );
01174                         if( srcRefNum   != 0 )  /* Close the source fork                */
01175                                 myverify_noerr( FSCloseFork( srcRefNum ) );
01176                 }                                       
01177         }
01178         while( err == noErr );
01179         
01180         if( err == errFSNoMoreItems )
01181                 err = noErr;
01182 
01183         mycheck_noerr( err );
01184                 
01185         return err;
01186 }
01187 
01188         /* If we're copying to a DropBox, we have to handle the copy process a little           */
01189         /* differently then when we are copying to a regular folder.                                            */
01190 static OSErr CopyForksToDropBox(        const FSRef             *source,
01191                                                                         const FSRef             *dest,
01192                                                                         CopyParams              *params )
01193 {
01194         GenLinkedList   forkList;
01195         GenIteratorPtr  pIter;
01196         ForkTrackerPtr  forkPtr;
01197         SInt16                  srcRefNum;
01198         OSErr                   err;
01199 
01200         InitLinkedList( &forkList, MyCloseForkProc );
01201         
01202                 /* If we're copying into a drop folder, open up all of those forks.     */
01203                 /* We have to do this because once we've started writing to a fork      */
01204                 /* in a drop folder, we can't open any more forks.                                      */
01205         err = OpenAllForks( dest, &forkList );
01206                 
01207                 /* Copy each fork over to the destination                                                       */
01208         for( InitIterator( &forkList, &pIter ); pIter != NULL && err == noErr; Next( &pIter ) )
01209         {
01210                 srcRefNum       = 0;
01211                 forkPtr         = GetData( pIter );
01212                                                                 /* Open the source fork         */
01213                 err = FSOpenFork(source, forkPtr->forkName.length, forkPtr->forkName.unicode, fsRdPerm, &srcRefNum);
01214                 if( err == noErr )              /* Write the data over          */
01215                         err = WriteFork( srcRefNum, forkPtr->forkDestRefNum, params, forkPtr->forkSize );
01216 
01217                 if( srcRefNum   != 0 )  /* Close the source fork        */
01218                         myverify_noerr( FSCloseFork( srcRefNum ) );
01219         }
01220                 /* we're done, so destroy the list even if an error occured                             */
01221                 /* the DisposeDataProc will close any open forks                                                */
01222         DestroyList( &forkList );
01223 
01224         mycheck_noerr( err );
01225 
01226         return err;
01227 }
01228 
01229 /*****************************************************************************/
01230 
01231         /* Create and open all the forks in the destination file.  We need to do this when              */
01232         /* we're copying into a drop folder, where you must open all the forks before starting  */
01233         /* to write to any of them.                                                                                                                             */
01234         /*                                                                                                                                                                              */
01235         /* IMPORTANT:  If it fails, this routine won't close forks that opened successfully.    */
01236         /*              Make sure that the DisposeDataProc for the forkList closed any open forks               */
01237         /*              Or you close each one manually before destroying the list                                               */
01238 static OSErr OpenAllForks(      const FSRef             *dest,
01239                                                         GenLinkedList   *forkList )
01240 {
01241         ForkTrackerPtr  forkPtr;
01242         HFSUniStr255    forkName;
01243         CatPositionRec  iterator;
01244         SInt64                  forkSize;
01245         OSErr                   err = ( dest != NULL && forkList != NULL ) ? noErr : paramErr;
01246         
01247                 /* need to initialize the iterator before using it */
01248         iterator.initialize = 0;
01249         
01250                 /* Iterate over the list of forks       */
01251         while( err == noErr )
01252         {
01253                 forkPtr = NULL; /* init forkPtr */
01254                 
01255                 err = FSIterateForks( dest, &iterator, &forkName, &forkSize, NULL );
01256                 if( err == noErr )
01257                         err = ( forkPtr = (ForkTrackerPtr) NewPtr( sizeof( ForkTracker ) ) ) != NULL ? noErr : MemError();
01258                 if( err == noErr )
01259                 {
01260                         forkPtr->forkName               = forkName;
01261                         forkPtr->forkSize               = forkSize;
01262                         forkPtr->forkDestRefNum = 0;
01263 
01264                                 /* Create the fork.  Note: Data and Resource forks are automatically            */
01265                                 /* created when the file is created.  FSCreateFork returns noErr for them       */
01266                                 /* We also want to create the fork even if there is no data to preserve         */
01267                                 /* empty forks                                                                                                                          */
01268                         err = FSCreateFork( dest, forkName.length, forkName.unicode );
01269 
01270                                 /* Swallow afpAccessDenied because this operation causes the external file      */
01271                                 /* system compatibility shim in Mac OS 9 to generate a GetCatInfo request       */
01272                                 /* to the AppleShare external file system, which in turn causes an AFP          */
01273                                 /* GetFileDirParms request on the wire, which the AFP server bounces with       */
01274                                 /* afpAccessDenied because the file is in a drop folder.  As there's no         */
01275                                 /* native support for non-classic forks in current AFP, there's no way I        */
01276                                 /* can decide how I should handle this in a non-test case.  So I just           */
01277                                 /* swallow the error and hope that when native AFP support arrives, the         */
01278                                 /* right thing will happen.                                                                                                     */
01279                         if( err == afpAccessDenied )
01280                                 err = noErr;
01281                                 
01282                                 /* only open the fork if the fork has some data                                                         */
01283                         if( err == noErr && forkPtr->forkSize > 0 )
01284                                 err = FSOpenFork( dest, forkPtr->forkName.length, forkPtr->forkName.unicode, fsWrPerm, &forkPtr->forkDestRefNum );
01285 
01286                                 /* if everything is ok, add this fork to the list                                                       */
01287                         if( err == noErr )
01288                                 err = AddToTail( forkList, forkPtr );
01289                 }
01290  
01291                 if( err != noErr && forkPtr != NULL )
01292                         DisposePtr( (char*) forkPtr );
01293         }
01294 
01295         if( err == errFSNoMoreItems )
01296                 err = noErr;
01297 
01298         mycheck_noerr( err );   
01299 
01300         return err;
01301 }
01302 
01303 /*****************************************************************************/
01304 
01305         /* Writes the fork from the source, references by srcRefNum, to the destination fork    */
01306         /* references by destRefNum                                                                                                                             */
01307 static OSErr WriteFork( const SInt16            srcRefNum,
01308                                                 const SInt16            destRefNum,
01309                                                 const CopyParams        *params,
01310                                                 const SInt64            forkSize )
01311 {
01312         UInt64                  bytesRemaining;
01313         UInt64                  bytesToReadThisTime;
01314         UInt64                  bytesToWriteThisTime;
01315         OSErr                   err;
01316         
01317 
01318                 /* Here we create space for the entire fork on the destination volume.                          */      
01319                 /* FSAllocateFork has the right semantics on both traditional Mac OS                            */
01320                 /* and Mac OS X.  On traditional Mac OS it will allocate space for the                          */
01321                 /* file in one hit without any other special action.  On Mac OS X,                                      */
01322                 /* FSAllocateFork is preferable to FSSetForkSize because it prevents                            */
01323                 /* the system from zero filling the bytes that were added to the end                            */
01324                 /* of the fork (which would be waste because we're about to write over                          */
01325                 /* those bytes anyway.                                                                                                                          */
01326         err = FSAllocateFork(destRefNum, kFSAllocNoRoundUpMask, fsFromStart, 0, forkSize, NULL);
01327 
01328                 /* Copy the file from the source to the destination in chunks of                                        */
01329                 /* no more than params->copyBufferSize bytes.  This is fairly                                           */
01330                 /* boring code except for the bytesToReadThisTime/bytesToWriteThisTime                          */
01331                 /* distinction.  On the last chunk, we round bytesToWriteThisTime                                       */
01332                 /* up to the next 512 byte boundary and then, after we exit the loop,                           */
01333                 /* we set the file's EOF back to the real location (if the fork size                            */
01334                 /* is not a multiple of 512 bytes).                                                                                                     */
01335                 /*                                                                                                                                                                      */
01336                 /* This technique works around a 'bug' in the traditional Mac OS File Manager,          */
01337                 /* where the File Manager will put the last 512-byte block of a large write into        */
01338                 /* the cache (even if we specifically request no caching) if that block is not          */
01339                 /* full. If the block goes into the cache it will eventually have to be                         */
01340                 /* flushed, which causes sub-optimal disk performance.                                                          */
01341                 /*                                                                                                                                                                      */
01342                 /* This is only done if the destination volume is local.  For a network                         */
01343                 /* volume, it's better to just write the last bytes directly.                                           */
01344                 /*                                                                                                                                                                      */
01345                 /* This is extreme over-optimization given the other limits of this                                     */
01346                 /* sample, but I will hopefully get to the other limits eventually.                                     */
01347         bytesRemaining = forkSize;
01348         while( err == noErr && bytesRemaining != 0 )
01349         {
01350                 if( bytesRemaining > params->copyBufferSize )
01351                 {
01352                         bytesToReadThisTime  =  params->copyBufferSize;
01353                         bytesToWriteThisTime =  bytesToReadThisTime;
01354                 }
01355                 else 
01356                 {
01357                         bytesToReadThisTime  =  bytesRemaining;
01358                         bytesToWriteThisTime =  ( params->copyingToLocalVolume )                  ?
01359                                                                         ( (bytesRemaining + 0x01FF ) & ~0x01FF ) : bytesRemaining;
01360                 }
01361                 
01362                 err = FSReadFork( srcRefNum, fsAtMark + noCacheMask, 0, bytesToReadThisTime, params->copyBuffer, NULL );
01363                 if( err == noErr )
01364                         err = FSWriteFork( destRefNum, fsAtMark + noCacheMask, 0, bytesToWriteThisTime, params->copyBuffer, NULL );
01365                 if( err == noErr )
01366                         bytesRemaining -= bytesToReadThisTime;
01367         }
01368         
01369         if (err == noErr && params->copyingToLocalVolume && ( forkSize & 0x01FF ) != 0 )
01370                 err = FSSetForkSize( destRefNum, fsFromStart, forkSize );
01371 
01372         return err;
01373 }
01374 
01375 /*****************************************************************************/
01376 
01377 #pragma mark ----- Calculate Buffer Size -----
01378 
01379         /* This routine calculates the appropriate buffer size for                              */
01380         /* the given volParms.  It's a simple composition of FSGetVolParms              */
01381         /* BufferSizeForVolSpeed.                                                                                               */
01382 static UInt32 CalcBufferSizeForVol(const GetVolParmsInfoBuffer *volParms, UInt32 volParmsSize)
01383 {
01384         UInt32  volumeBytesPerSecond = 0;
01385 
01386         /* Version 1 of the GetVolParmsInfoBuffer included the vMAttrib         */
01387         /* field, so we don't really need to test actualSize.  A noErr          */
01388         /* result indicates that we have the info we need.  This is                     */
01389         /* just a paranoia check.                                                                                       */
01390         
01391         mycheck(volParmsSize >= offsetof(GetVolParmsInfoBuffer, vMVolumeGrade));
01392 
01393         /* On the other hand, vMVolumeGrade was not introduced until            */
01394         /* version 2 of the GetVolParmsInfoBuffer, so we have to explicitly     */
01395         /* test whether we got a useful value.                                                          */
01396         
01397         if( ( volParmsSize >= offsetof(GetVolParmsInfoBuffer, vMForeignPrivID) ) &&
01398                 ( volParms->vMVolumeGrade <= 0 ) ) 
01399         {
01400                 volumeBytesPerSecond = -volParms->vMVolumeGrade;
01401         }
01402 
01403         return BufferSizeForVolSpeed(volumeBytesPerSecond);
01404 }
01405 
01406 /*****************************************************************************/
01407 
01408         /* Calculate an appropriate copy buffer size based on the volumes               */
01409         /* rated speed.  Our target is to use a buffer that takes 0.25                  */
01410         /* seconds to fill.  This is necessary because the volume might be              */
01411         /* mounted over a very slow link (like ARA), and if we do a 256 KB              */
01412         /* read over an ARA link we'll block the File Manager queue for                 */
01413         /* so long that other clients (who might have innocently just                   */
01414         /* called PBGetCatInfoSync) will block for a noticeable amount of time. */
01415         /*                                                                                                                                              */
01416         /* Note that volumeBytesPerSecond might be 0, in which case we assume   */
01417         /* some default value.                                                                                                  */
01418 static UInt32 BufferSizeForVolSpeed(UInt32 volumeBytesPerSecond)
01419 {
01420         ByteCount bufferSize;
01421         
01422         if (volumeBytesPerSecond == 0)
01423                 bufferSize = kDefaultCopyBufferSize;
01424         else
01425         {       /* We want to issue a single read that takes 0.25 of a second,  */
01426                 /* so devide the bytes per second by 4.                                                 */
01427                 bufferSize = volumeBytesPerSecond / 4;
01428         }
01429         
01430                 /* Round bufferSize down to 512 byte boundary. */
01431         bufferSize &= ~0x01FF;
01432         
01433                 /* Clip to sensible limits. */
01434         if (bufferSize < kMinimumCopyBufferSize)
01435                 bufferSize = kMinimumCopyBufferSize;
01436         else if (bufferSize > kMaximumCopyBufferSize)
01437                 bufferSize = kMaximumCopyBufferSize;
01438                 
01439         return bufferSize;
01440 }
01441 
01442 /*****************************************************************************/
01443 
01444 #pragma mark ----- Delete Objects -----
01445 
01446 OSErr FSDeleteObjects( const FSRef *source )
01447 {
01448         FSCatalogInfo   catalogInfo;
01449         OSErr                   err = ( source != NULL ) ? noErr : paramErr;
01450         
01451         #if DEBUG && !TARGET_API_MAC_OS8
01452         if( err == noErr )
01453         {
01454                 char    path[1024];             /* Flawfinder: ignore */
01455                 myverify_noerr(FSRefMakePath( source,   (unsigned char*)path, 1024 ));
01456                 dwarning(("\n%s -- Deleting %s\n", __FUNCTION__, path));
01457         }       
01458         #endif
01459 
01460                 /* get nodeFlags for container */
01461         if( err == noErr )
01462                 err = FSGetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL,NULL);
01463         if( err == noErr && (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0 )
01464         {               /* its a directory, so delete its contents before we delete it */
01465                 err = FSDeleteFolder(source);
01466         }
01467         if( err == noErr && (catalogInfo.nodeFlags & kFSNodeLockedMask) != 0 )  /* is object locked? */
01468         {               /* then attempt to unlock the object (ignore err since FSDeleteObject will set it correctly) */
01469                 catalogInfo.nodeFlags &= ~kFSNodeLockedMask;
01470                 (void) FSSetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo);
01471         }               
01472         if( err == noErr )      /* delete the object (if it was a directory it is now empty, so we can delete it) */
01473                 err = FSDeleteObject(source);
01474 
01475         mycheck_noerr( err );
01476         
01477         return ( err );
01478 }
01479 
01480 /*****************************************************************************/
01481 
01482 #pragma mark ----- Delete Folders -----
01483 
01484 static OSErr FSDeleteFolder( const FSRef *container )
01485 {
01486         FSDeleteObjectGlobals   theGlobals;
01487         
01488         theGlobals.result = ( container != NULL ) ? noErr : paramErr;
01489         
01490                 /* delete container's contents */
01491         if( theGlobals.result == noErr )
01492                 FSDeleteFolderLevel(container, &theGlobals);
01493         
01494         mycheck_noerr( theGlobals.result );
01495         
01496         return ( theGlobals.result );
01497 }
01498 
01499 /*****************************************************************************/
01500 
01501 static void FSDeleteFolderLevel(const FSRef                             *container,
01502                                                                 FSDeleteObjectGlobals   *theGlobals )
01503 {
01504         FSIterator                                      iterator;
01505         FSRef                                           itemToDelete;
01506         UInt16                                          nodeFlags;
01507 
01508                 /* Open FSIterator for flat access and give delete optimization hint */
01509         theGlobals->result = FSOpenIterator(container, kFSIterateFlat + kFSIterateDelete, &iterator);
01510         if ( theGlobals->result == noErr )
01511         {
01512                 do      /* delete the contents of the directory */
01513                 {
01514                                 /* get 1 item to delete */
01515                         theGlobals->result = FSGetCatalogInfoBulk(      iterator, 1, &theGlobals->actualObjects,
01516                                                                                                                 NULL, kFSCatInfoNodeFlags, &theGlobals->catalogInfo,
01517                                                                                                                 &itemToDelete, NULL, NULL);
01518                         if ( (theGlobals->result == noErr) && (theGlobals->actualObjects == 1) )
01519                         {
01520                                         /* save node flags in local in case we have to recurse */
01521                                 nodeFlags = theGlobals->catalogInfo.nodeFlags;
01522                                 
01523                                         /* is it a directory? */
01524                                 if ( (nodeFlags & kFSNodeIsDirectoryMask) != 0 )
01525                                 {       /* yes -- delete its contents before attempting to delete it */ 
01526                                         FSDeleteFolderLevel(&itemToDelete, theGlobals);
01527                                 }
01528                                 if ( theGlobals->result == noErr)                       /* are we still OK to delete? */
01529                                 {       
01530                                         if ( (nodeFlags & kFSNodeLockedMask) != 0 )     /* is item locked? */
01531                                         {               /* then attempt to unlock it (ignore result since FSDeleteObject will set it correctly) */
01532                                                 theGlobals->catalogInfo.nodeFlags = nodeFlags & ~kFSNodeLockedMask;
01533                                                 (void) FSSetCatalogInfo(&itemToDelete, kFSCatInfoNodeFlags, &theGlobals->catalogInfo);
01534                                         }
01535                                                 /* delete the item */
01536                                         theGlobals->result = FSDeleteObject(&itemToDelete);
01537                                 }
01538                         }
01539                 } while ( theGlobals->result == noErr );
01540                         
01541                         /* we found the end of the items normally, so return noErr */
01542                 if ( theGlobals->result == errFSNoMoreItems )
01543                         theGlobals->result = noErr;
01544                         
01545                         /* close the FSIterator (closing an open iterator should never fail) */
01546                 myverify_noerr(FSCloseIterator(iterator));
01547         }
01548 
01549         mycheck_noerr( theGlobals->result );
01550         
01551         return;
01552 }
01553 
01554 /*****************************************************************************/
01555 
01556 #pragma mark ----- Utilities -----
01557 
01558         /* Figures out if the given directory is a drop box or not              */
01559         /* if it is, the Copy Engine will behave slightly differently   */
01560 static OSErr IsDropBox( const FSRef* source,
01561                                                 Boolean *isDropBox )
01562 {
01563         FSCatalogInfo                   tmpCatInfo;
01564         FSSpec                                  sourceSpec;
01565         Boolean                                 isDrop = false;
01566         OSErr                                   err;
01567         
01568                 /* get info about the destination, and an FSSpec to it for PBHGetDirAccess */
01569         err = FSGetCatalogInfo(source, kFSCatInfoNodeFlags | kFSCatInfoPermissions, &tmpCatInfo, NULL, &sourceSpec, NULL);
01570         if( err == noErr )      /* make sure the source is a directory */
01571                 err = ((tmpCatInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) ? noErr : errFSNotAFolder;
01572         if( err == noErr )
01573         {
01574                 HParamBlockRec  hPB;
01575 
01576                 BlockZero( &hPB, sizeof( HParamBlockRec ) );
01577 
01578                 hPB.accessParam.ioNamePtr               = sourceSpec.name;
01579                 hPB.accessParam.ioVRefNum               = sourceSpec.vRefNum;
01580                 hPB.accessParam.ioDirID                 = sourceSpec.parID;
01581                 
01582                         /* This is the official way (reads: the way X Finder does it) to figure */
01583                         /* out the current users access privileges to a given directory                 */
01584                 err = PBHGetDirAccessSync(&hPB);
01585                 if( err == noErr )      /* its a drop folder if the current user only has write access */
01586                         isDrop = (hPB.accessParam.ioACAccess & kPrivilegesMask) == kioACAccessUserWriteMask;
01587                 else if ( err == paramErr )
01588                 {
01589                         /* There is a bug (2908703) in the Classic File System (not OS 9.x or Carbon)   */
01590                         /* on 10.1.x where PBHGetDirAccessSync sometimes returns paramErr even when the */
01591                         /* data passed in is correct.  This is a workaround/hack for that problem,              */
01592                         /* but is not as accurate.                                                                                                              */
01593                         /* Basically, if "Everyone" has only Write/Search access then its a drop folder */
01594                         /* that is the most common case when its a drop folder                                                  */
01595                         FSPermissionInfo *tmpPerm = (FSPermissionInfo *)tmpCatInfo.permissions;
01596                         isDrop = ((tmpPerm->mode & kRWXOtherAccessMask) == kDropFolderValue);
01597                         err = noErr;
01598                 }
01599         }
01600 
01601         *isDropBox = isDrop;
01602 
01603         mycheck_noerr( err );
01604         
01605         return err;
01606 }
01607 
01608 /*****************************************************************************/
01609 
01610         /* The copy engine is going to set the item's creation date                     */
01611         /* to kMagicBusyCreationDate while it's copying the item.                       */
01612         /* But kMagicBusyCreationDate is an old-style 32-bit date/time,         */
01613         /* while the HFS Plus APIs use the new 64-bit date/time.  So            */
01614         /* we have to call a happy UTC utilities routine to convert from        */
01615         /* the local time kMagicBusyCreationDate to a UTCDateTime                       */
01616         /* gMagicBusyCreationDate, which the File Manager will store            */
01617         /* on disk and which the Finder we read back using the old                      */
01618         /* APIs, whereupon the File Manager will convert it back                        */
01619         /* to local time (and hopefully get the kMagicBusyCreationDate          */
01620         /* back!).                                                                                                                      */
01621 static OSErr GetMagicBusyCreateDate( UTCDateTime *date )
01622 {
01623         static  UTCDateTime     magicDate       = { 0, 0xDEADBEEF, 0 };
01624                         OSErr           err             = ( date != NULL ) ? noErr : paramErr;
01625         
01626         if( err == noErr && magicDate.lowSeconds == 0xDEADBEEF )
01627                 err = ConvertLocalTimeToUTC( kMagicBusyCreationDate, &magicDate.lowSeconds );
01628         if( err == noErr )
01629                 *date = magicDate;
01630                 
01631         mycheck_noerr( err );   
01632 
01633         return err;             
01634 }
01635 
01636 /*****************************************************************************/
01637 
01638 static OSErr FSGetVRefNum(      const FSRef             *ref,
01639                                                         FSVolumeRefNum  *vRefNum)
01640 {
01641         FSCatalogInfo   catalogInfo;
01642         OSErr                   err = ( ref != NULL && vRefNum != NULL ) ? noErr : paramErr;
01643 
01644         if( err == noErr )      /* get the volume refNum from the FSRef */
01645                 err = FSGetCatalogInfo(ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
01646         if( err == noErr )
01647                 *vRefNum = catalogInfo.volume;
01648                 
01649         mycheck_noerr( err );
01650 
01651         return err;
01652 }
01653 
01654 /*****************************************************************************/ 
01655 
01656 static OSErr FSGetVolParms(     FSVolumeRefNum                  volRefNum,
01657                                                         UInt32                                  bufferSize,
01658                                                         GetVolParmsInfoBuffer   *volParmsInfo,
01659                                                         UInt32                                  *actualInfoSize)                /*      Can Be NULL     */
01660 {
01661         HParamBlockRec  pb;
01662         OSErr                   err = ( volParmsInfo != NULL ) ? noErr : paramErr;
01663                 
01664         if( err == noErr )
01665         {
01666                 pb.ioParam.ioNamePtr = NULL;
01667                 pb.ioParam.ioVRefNum = volRefNum;
01668                 pb.ioParam.ioBuffer = (Ptr)volParmsInfo;
01669                 pb.ioParam.ioReqCount = (SInt32)bufferSize;
01670                 err = PBHGetVolParmsSync(&pb);
01671         }
01672                 /* return number of bytes the file system returned in volParmsInfo buffer */
01673         if( err == noErr && actualInfoSize != NULL)
01674                 *actualInfoSize = (UInt32)pb.ioParam.ioActCount;
01675 
01676         mycheck_noerr( err );   
01677 
01678         return ( err );
01679 }
01680 
01681 /*****************************************************************************/
01682 
01683 /* Converts a unicode string to a PString                                                                               */
01684 /* If your code is only for OS X, you can use CFString functions to do all this */
01685 /* Since this sample code supports OS 9.1 -> OS X, I have to do this the                */
01686 /* old fashioned way.                                                                                                                   */
01687 static OSErr UniStrToPStr(      const HFSUniStr255      *uniStr,
01688                                                         TextEncoding             textEncodingHint,
01689                                                         Boolean                          isVolumeName,
01690                                                         Str255                           pStr )
01691 {
01692         UnicodeMapping          uMapping;
01693         UnicodeToTextInfo       utInfo;
01694         ByteCount                       unicodeByteLength = 0;
01695         ByteCount                       unicodeBytesConverted;
01696         ByteCount                       actualPascalBytes;
01697         OSErr                           err = (uniStr != NULL && pStr != NULL) ? noErr : paramErr;
01698 
01699                 /* make sure output is valid in case we get errors or there's nothing to convert */
01700         pStr[0] = 0;
01701 
01702         if( err == noErr )
01703                 unicodeByteLength = uniStr->length * sizeof(UniChar); /* length can be zero, which is fine */
01704         if( err == noErr && unicodeByteLength != 0 )
01705         {
01706                         /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */
01707                 if ( kTextEncodingUnknown == textEncodingHint )
01708                 {
01709                         ScriptCode                      script;
01710                         RegionCode                      region;
01711                         
01712                         script = (ScriptCode)GetScriptManagerVariable(smSysScript);
01713                         region = (RegionCode)GetScriptManagerVariable(smRegionCode);
01714                         err = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, 
01715                                                                                                         region, NULL, &textEncodingHint );
01716                         if ( err == paramErr )
01717                         {               /* ok, ignore the region and try again */
01718                                 err = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare,
01719                                                                                                                 kTextRegionDontCare, NULL, 
01720                                                                                                                 &textEncodingHint );
01721                         }
01722                         if ( err != noErr )                     /* ok... try something */
01723                                 textEncodingHint = kTextEncodingMacRoman;               
01724                 }
01725                 
01726                 uMapping.unicodeEncoding        = CreateTextEncoding(   kTextEncodingUnicodeV2_0,
01727                                                                                                                         kUnicodeCanonicalDecompVariant, 
01728                                                                                                                         kUnicode16BitFormat);
01729                 uMapping.otherEncoding          = GetTextEncodingBase(textEncodingHint);
01730                 uMapping.mappingVersion         = kUnicodeUseHFSPlusMapping;
01731         
01732                 err = CreateUnicodeToTextInfo(&uMapping, &utInfo);
01733                 if( err == noErr )
01734                 {
01735                         err = ConvertFromUnicodeToText( utInfo, unicodeByteLength, uniStr->unicode, kUnicodeLooseMappingsMask,
01736                                                                                                 0, NULL, 0, NULL,       /* offsetCounts & offsetArrays */
01737                                                                                                 isVolumeName ? kHFSMaxVolumeNameChars : kHFSPlusMaxFileNameChars,
01738                                                                                                 &unicodeBytesConverted, &actualPascalBytes, &pStr[1]);
01739                 }
01740                 if( err == noErr )
01741                         pStr[0] = actualPascalBytes;
01742                 
01743                         /* verify the result in debug builds -- there's really not anything you can do if it fails */
01744                 myverify_noerr(DisposeUnicodeToTextInfo(&utInfo));                              
01745         }
01746         
01747         mycheck_noerr( err );
01748         
01749         return ( err ); 
01750 }
01751 
01752 /*****************************************************************************/
01753 
01754         /* Yeah I know there is FSpMakeFSRef, but this way I don't have to      */
01755         /* actually have an FSSpec created to make the FSRef, and this is       */
01756         /* what FSpMakeFSRef does anyways                                                                       */
01757 static OSErr FSMakeFSRef(       FSVolumeRefNum          volRefNum,
01758                                                         SInt32                          dirID,
01759                                                         ConstStr255Param        name,
01760                                                         FSRef                           *ref )
01761 {
01762         FSRefParam      pb;
01763         OSErr           err = ( ref != NULL ) ? noErr : paramErr;
01764         
01765         if( err == noErr )
01766         {
01767                 pb.ioVRefNum = volRefNum;
01768                 pb.ioDirID = dirID;
01769                 pb.ioNamePtr = (StringPtr)name;
01770                 pb.newRef = ref;
01771                 err = PBMakeFSRefSync(&pb);
01772         }
01773         
01774         mycheck_noerr( err );   
01775                 
01776         return ( err );
01777 }
01778 
01779 /*****************************************************************************/
01780 
01781         /* This checks the destination to see if an object of the same name as the source       */
01782         /* exists or not.  If it does we have to special handle the DupeActions                         */
01783         /*                                                                                                                                                                      */
01784         /* If kDupeActionReplace we move aside the object by renameing it to ".DeleteMe"        */
01785         /* so that it will be invisible (X only), and give a suggestion on what to do with      */
01786         /* it if for some unknown reason it survives the copy and the user finds it.  This      */
01787         /* rename is mainly done to handle the case where the source is in the destination      */
01788         /* and the user wants to replace.  Basically keeping the source around throughout       */
01789         /* the copy, deleting it afterwards.  Its also done cause its a good idea not to        */
01790         /* dispose of the existing object in case the copy fails                                                        */
01791         /*                                                                                                                                                                      */
01792         /* If kDupeActionRename, we create a unique name for the new object and pass            */
01793         /* it back to the caller                                                                                                                        */
01794 static OSErr SetupDestination(  const FSRef                     *destDir,
01795                                                                 const DupeAction        dupeAction,
01796                                                                 HFSUniStr255            *sourceName,
01797                                                                 FSRef                           *deleteMeRef,
01798                                                                 Boolean                         *isReplacing )
01799 {
01800         FSRef   tmpRef;
01801         OSErr   err;
01802 
01803                 /* check if an object of the same name already exists in the destination */
01804         err = FSMakeFSRefUnicode( destDir, sourceName->length, sourceName->unicode, kTextEncodingUnknown, &tmpRef );
01805         if( err == noErr )
01806         {                                                                                                       /* if the user wants to replace the existing            */
01807                                                                                                                 /* object, rename it to .DeleteMe first.  Delete it     */
01808                 if( dupeAction == kDupeActionReplace )                  /* only after copying the new one successfully          */
01809                 {
01810                         err = FSRenameUnicode( &tmpRef, 9, (UniChar*)"\0.\0D\0e\0l\0e\0t\0e\0M\0e", kTextEncodingMacRoman, deleteMeRef );
01811                         *isReplacing = ( err == noErr ) ? true : false;
01812                 }
01813                 else if( dupeAction == kDupeActionRename )              /* if the user wants to just rename it                          */
01814                         err = GetUniqueName( destDir, sourceName );     /* then we get a unique name for the new object         */
01815         }
01816         else if ( err == fnfErr )                                                       /* if no object exists then                                                     */
01817                 err = noErr;                                                                    /* continue with no error                                                       */
01818         
01819         return err;
01820 }
01821 
01822 /*****************************************************************************/
01823 
01824         /* Given a directory and a name, GetUniqueName will check if an object  */
01825         /* with the same name already exists, and if it does it will create             */
01826         /* a new, unique name for and return it.                                                                */
01827         /* it simply appends a number to the end of the name.  It is not                */
01828         /* fool proof, and it is limited...  I'll take care of that in a                */
01829         /* later release                                                                                                                */
01830         /* If anyone has any suggestions/better techniques I would love to hear */
01831         /* about them                                                                                                                   */
01832 static OSErr GetUniqueName(     const FSRef             *destDir,
01833                                                         HFSUniStr255    *sourceName )
01834 {
01835         HFSUniStr255    tmpName = *sourceName;
01836         FSRef                   tmpRef;
01837         unsigned char   hexStr[17] = "123456789";               /* Flawfinder: ignore */                /* yeah, only 9...  I'm lazy, sosumi */
01838         long                    count = 0;
01839         int                             index;
01840         OSErr                   err;    
01841 
01842                 /* find the dot, if there is one */
01843         for( index = tmpName.length; index >= 0 && tmpName.unicode[index] != (UniChar) '.'; index-- ) { /* Do Nothing */ }              
01844         
01845         if( index <= 0) /* no dot or first char is a dot (invisible file), so append to end of name */
01846                 index = tmpName.length;
01847         else                    /* shift the extension up two spots to make room for our digits */
01848                 BlockMoveData( tmpName.unicode + index, tmpName.unicode + index + 2, (tmpName.length - index) * 2 );
01849                 
01850                 /* add the space to the name */
01851         tmpName.unicode[ index ] = (UniChar)' ';
01852                 /* we're adding two characters to the name */
01853         tmpName.length += 2;
01854 
01855         do {    /* add the digit to the name */
01856                 tmpName.unicode[ index + 1 ] = hexStr[count];
01857                         /* check if the file with this new name already exists */
01858                 err = FSMakeFSRefUnicode( destDir, tmpName.length, tmpName.unicode, kTextEncodingUnknown, &tmpRef );
01859                 count++;
01860         } while( err == noErr && count < 10 );
01861 
01862         if( err == fnfErr )
01863         {
01864                 err = noErr;
01865                 *sourceName = tmpName;
01866         }
01867         
01868         return err;
01869 }
01870 
01871 /*****************************************************************************/
01872 
01873 static OSErr GetObjectName( const FSRef                 *sourceRef,
01874                                                         HFSUniStr255            *sourceName,            /* can be NULL */
01875                                                         TextEncoding            *sourceEncoding )       /* can be NULL */
01876 {
01877         FSCatalogInfo           catInfo;
01878         FSCatalogInfoBitmap     whichInfo = (sourceEncoding != NULL) ? kFSCatInfoTextEncoding : kFSCatInfoNone;
01879         OSErr                           err;
01880         
01881         err = FSGetCatalogInfo( sourceRef, whichInfo, &catInfo, sourceName, NULL, NULL );
01882         if( err == noErr && sourceEncoding != NULL )
01883                 *sourceEncoding = catInfo.textEncodingHint;
01884         
01885         return err;
01886 }
01887 
01888 /*****************************************************************************/
01889 
01890 static OSErr CreateFolder(      const FSRef                     *sourceRef,
01891                                                         const FSRef                     *destDirRef,
01892                                                         const FSCatalogInfo     *catalogInfo,
01893                                                         const HFSUniStr255      *folderName,
01894                                                         CopyParams                      *params,
01895                                                         FSRef                           *newFSRefPtr,
01896                                                         FSSpec                          *newFSSpecPtr )
01897 {
01898         FSCatalogInfo           tmpCatInfo;
01899         FSPermissionInfo        origPermissions;
01900         OSErr                           err = ( sourceRef != NULL && destDirRef != NULL && catalogInfo != NULL &&
01901                                                                 folderName != NULL && newFSRefPtr != NULL ) ? noErr : paramErr;
01902 
01903         if( err == noErr )
01904         {               /* store away the catInfo, create date and permissions on the orig folder */
01905                 tmpCatInfo = *catalogInfo;
01906                 origPermissions = *((FSPermissionInfo*)catalogInfo->permissions);
01907         }
01908         if( err == noErr )                      /* create the new folder */
01909                 err = DoCreateFolder( sourceRef, destDirRef, &tmpCatInfo, folderName, params, newFSRefPtr, newFSSpecPtr ); 
01910         if( err == noErr && !params->copyingToDropFolder )
01911         {                       /* if its not a drop box, set the permissions on the new folder */
01912                 *((FSPermissionInfo*)tmpCatInfo.permissions)    = origPermissions;
01913                 err = FSSetCatalogInfo( newFSRefPtr, kFSCatInfoPermissions, &tmpCatInfo );
01914         }
01915         
01916         mycheck_noerr( err );
01917         
01918         return err;
01919 }
01920 
01921 /*****************************************************************************/
01922 
01923 static OSErr DoCreateFolder(const FSRef                 *sourceRef,
01924                                                         const FSRef                     *destDirRef,
01925                                                         const FSCatalogInfo     *catalogInfo,
01926                                                         const HFSUniStr255      *folderName,
01927                                                         CopyParams                      *params,
01928                                                         FSRef                           *newFSRefPtr,
01929                                                         FSSpec                          *newFSSpecPtr)
01930 {
01931         FSCatalogInfo   catInfo = *catalogInfo;
01932         OSErr                   err;
01933         
01934                 /* Clear the "inited" bit so that the Finder positions the icon for us. */
01935         ((FInfo *)(catInfo.finderInfo))->fdFlags &= ~kHasBeenInited;
01936                 
01937                 /* we need to have user level read/write/execute access to the folder we are going to create,   */
01938                 /* otherwise FSCreateDirectoryUnicode will return -5000 (afpAccessDenied),                                              */
01939                 /* and the FSRef returned will be invalid, yet the folder is created...  bug?                                   */
01940         ((FSPermissionInfo*) catInfo.permissions)->mode |= kRWXUserAccessMask;
01941         
01942         err = FSCreateDirectoryUnicode( destDirRef, folderName->length,
01943                                                                         folderName->unicode, kFSCatInfoSettableInfo,
01944                                                                         &catInfo, newFSRefPtr,
01945                                                                         newFSSpecPtr, NULL);
01946                                                                         
01947                 /* With the new APIs, folders can have forks as well as files.  Before  */
01948                 /* we start copying items in the folder, we     must copy over the forks        */
01949                 /* Currently, MacOS doesn't support any file systems that have forks in */
01950                 /* folders, but the API supports it so (for possible future                             */
01951                 /* compatability) I kept this in here.                                                                  */
01952         if( err == noErr )
01953                 err = CopyForks( sourceRef, newFSRefPtr, params );
01954         
01955         mycheck_noerr( err );
01956 
01957         return err;
01958 }
01959 
01960 /*****************************************************************************/
01961 
01962         /* This is the DisposeDataProc that is used by the GenLinkedList in FSCopyFolder        */
01963         /* Simply disposes of the data we created and returns                                                           */
01964 static pascal void MyDisposeDataProc( void *pData )
01965 {
01966         if( pData != NULL )
01967                 DisposePtr( (char*) pData );
01968 }
01969 
01970 /*****************************************************************************/
01971 
01972         /* This is the DisposeDataProc that is used by the GenLinkedList in CopyItemForks       */
01973         /* Simply closes the resource fork (if opened, != 0) and disposes of the memory         */
01974 static pascal void MyCloseForkProc( void *pData )
01975 {
01976         SInt16          refNum;
01977 
01978         if( pData == NULL )
01979                 return;
01980                 
01981         refNum = ((ForkTrackerPtr)pData)->forkDestRefNum;
01982         if( refNum != 0 )
01983                 myverify_noerr( FSCloseFork( refNum ) );        /* the fork was opened, so close it */
01984         
01985         DisposePtr( (char*) pData );    
01986 }

Generated on Thu Jul 1 06:08:17 2010 for Second Life Viewer by  doxygen 1.4.7