Back to index

lightning-sunbird  0.9+nobinonly
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 the HFS+ API's.
00005                             This code is a combination of MoreFilesX and MPFileCopy 
00006                             with some added features.  This code will run on OS 9.1 and up
00007                             and 10.1.x (Classic and Carbon)
00008 
00009        Disclaimer:   IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
00010                             ("Apple") in consideration of your agreement to the following terms, and your
00011                             use, installation, modification or redistribution of this Apple software
00012                             constitutes acceptance of these terms.  If you do not agree with these terms,
00013                             please do not use, install, modify or redistribute this Apple software.
00014 
00015                             In consideration of your agreement to abide by the following terms, and subject
00016                             to these terms, Apple grants you a personal, non-exclusive license, under Apple’s
00017                             copyrights in this original Apple software (the "Apple Software"), to use,
00018                             reproduce, modify and redistribute the Apple Software, with or without
00019                             modifications, in source and/or binary forms; provided that if you redistribute
00020                             the Apple Software in its entirety and without modifications, you must retain
00021                             this notice and the following text and disclaimers in all such redistributions of
00022                             the Apple Software.  Neither the name, trademarks, service marks or logos of
00023                             Apple Computer, Inc. may be used to endorse or promote products derived from the
00024                             Apple Software without specific prior written permission from Apple.  Except as
00025                             expressly stated in this notice, no other rights or licenses, express or implied,
00026                             are granted by Apple herein, including but not limited to any patent rights that
00027                             may be infringed by your derivative works or by other works in which the Apple
00028                             Software may be incorporated.
00029 
00030                             The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
00031                             WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
00032                             WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00033                             PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
00034                             COMBINATION WITH YOUR PRODUCTS.
00035 
00036                             IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
00037                             CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00038                             GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00039                             ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
00040                             OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
00041                             (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
00042                             ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00043 
00044        Copyright © 2002 Apple Computer, Inc., All Rights Reserved
00045 */
00046 
00047 #include "FSCopyObject.h"
00048 #include <UnicodeConverter.h>
00049 #include <stddef.h>
00050 #include <string.h>
00051 
00052 /*
00053        
00054 */
00055 
00056 #pragma mark ----- Tunable Parameters -----
00057 
00058 // The following constants control the behavior of the copy engine.
00059 
00060 enum {        // BufferSizeForThisVolumeSpeed
00061 //     kDefaultCopyBufferSize =   2L * 1024 * 1024,                   // Fast be not very responsive.
00062        kDefaultCopyBufferSize = 256L * 1024,                                 // Slower, but can still use machine.
00063        kMaximumCopyBufferSize =   2L * 1024 * 1024,
00064        kMinimumCopyBufferSize = 1024
00065 };
00066 
00067 enum {        // CalculateForksToCopy
00068        kExpectedForkCount     = 10                      // Number of non-classic forks we expect.
00069 };                                                                           // (i.e. non resource/data forks) 
00070 
00071 enum {        // CheckForDestInsideSource
00072        errFSDestInsideSource = -1234
00073 };
00074 
00075 enum {        
00076                      // for use with PBHGetDirAccess in IsDropBox
00077        kPrivilegesMask                    = kioACAccessUserWriteMask | kioACAccessUserReadMask | kioACAccessUserSearchMask,
00078 
00079                      // for use with FSGetCatalogInfo and FSPermissionInfo->mode
00080                      // from sys/stat.h...  note -- sys/stat.h definitions are in octal
00081                      //
00082                      // You can use these values to adjust the users/groups permissions
00083                      // on a file/folder with FSSetCatalogInfo and extracting the
00084                      // kFSCatInfoPermissions field.  See code below for examples
00085        kRWXUserAccessMask          = 0x01C0,
00086        kReadAccessUser                    = 0x0100,
00087        kWriteAccessUser            = 0x0080,
00088        kExecuteAccessUser          = 0x0040,
00089 
00090        kRWXGroupAccessMask         = 0x0038,
00091        kReadAccessGroup            = 0x0020,
00092        kWriteAccessGroup           = 0x0010,
00093        kExecuteAccessGroup         = 0x0008,
00094 
00095        kRWXOtherAccessMask         = 0x0007,
00096        kReadAccessOther            = 0x0004,
00097        kWriteAccessOther           = 0x0002,
00098        kExecuteAccessOther         = 0x0001,
00099 
00100        kDropFolderValue            = kWriteAccessOther | kExecuteAccessOther
00101 };
00102 
00103 #pragma mark ----- Struct Definitions -----
00104 
00105 #define VolHasCopyFile(volParms) \
00106               (((volParms)->vMAttrib & (1L << bHasCopyFile)) != 0)
00107 
00108        // The CopyParams data structure holds the copy buffer used
00109        // when copying the forks over, as well as special case
00110        // info on the destination
00111 struct CopyParams {
00112        UTCDateTime          magicBusyCreateDate;
00113        void                 *copyBuffer;
00114        ByteCount            copyBufferSize;
00115        Boolean         copyingToDropFolder;
00116        Boolean                     copyingToLocalVolume;
00117 };
00118 typedef struct CopyParams CopyParams;
00119 
00120        // The FilterParams data structure holds the date and info
00121        // that the caller wants passed into the Filter Proc, as well
00122        // as the Filter Proc Pointer itself
00123 struct FilterParams {
00124        FSCatalogInfoBitmap                       whichInfo;
00125        CopyObjectFilterProcPtr                   filterProcPtr;
00126        FSSpec                                           fileSpec;
00127        FSSpec                                       *fileSpecPtr;
00128        HFSUniStr255                              fileName;
00129        HFSUniStr255                          *fileNamePtr;
00130        void                                         *yourDataPtr;
00131 };
00132 typedef struct FilterParams FilterParams;
00133 
00134        // The ForkTracker data structure holds information about a specific fork,
00135        // specifically the name and the refnum.  We use this to build a list of
00136        // all the forks before we start copying them.  We need to do this because,
00137        // if we're copying into a drop folder, we must open all the forks before
00138        // we start copying data into any of them.
00139        // Plus it's a convenient way to keep track of all the forks...
00140 struct ForkTracker {
00141        HFSUniStr255  forkName;
00142        SInt64               forkSize;
00143        SInt16        forkDestRefNum;
00144 };
00145 typedef struct ForkTracker ForkTracker;
00146 typedef ForkTracker *ForkTrackerPtr;
00147 
00148        // The FSCopyObjectGlobals data structure holds information needed to do
00149        // the recursive copy of a directory.
00150 struct FSCopyObjectGlobals
00151 {
00152        FSCatalogInfo catalogInfo;
00153        FSRef                ref;                 /* FSRef to the source file/folder*/
00154        FSRef                destRef;             /* FSRef to the destination directory */
00155        CopyParams           *copyParams;  /* pointer to info needed to do the copy */
00156        FilterParams  *filterParams;       /* pointer to info needed for the optional filter proc */
00157        ItemCount            maxLevels;           /* maximum levels to iterate through */
00158        ItemCount            currentLevel; /* the current level FSCopyFolderLevel is on */
00159        Boolean                     quitFlag;            /* set to true if filter wants to kill interation */
00160        Boolean                     containerChanged; /* temporary - set to true if the current container changed during iteration */
00161        OSErr                result;                     /* result */
00162        ItemCount            actualObjects;       /* number of objects returned */
00163 };
00164 typedef struct FSCopyObjectGlobals FSCopyObjectGlobals;
00165 
00166        // The FSDeleteObjectGlobals data structure holds information needed to 
00167        // recursively delete a directory
00168 struct FSDeleteObjectGlobals
00169 {
00170        FSCatalogInfo                             catalogInfo;  /* FSCatalogInfo */
00171        ItemCount                                        actualObjects;       /* number of objects returned */
00172        OSErr                                            result;                     /* result */
00173 };
00174 typedef struct FSDeleteObjectGlobals FSDeleteObjectGlobals;
00175 
00176 #pragma mark ----- Local Prototypes -----
00177 
00178 static OSErr         FSCopyFile(   const FSRef          *source,
00179                                                         const FSRef          *destDir,
00180                                                         const HFSUniStr255 *destName,                    /* can be NULL (no rename during copy) */
00181                                                         CopyParams           *copyParams,
00182                                                         FilterParams  *filterParams,
00183                                                         FSRef                *newFile);                         /* can be NULL */
00184                                                         
00185 static OSErr         CopyFile(     const FSRef                 *source,
00186                                                         FSCatalogInfo        *sourceCatInfo,
00187                                                         const FSRef                 *destDir,
00188                                                         const HFSUniStr255   *destName,
00189                                                         CopyParams                  *copyParams,
00190                                                         FSRef                       *newRef);                   /* can be NULL */
00191                                                         
00192 static OSErr         FSUsePBHCopyFile(    const FSRef          *srcFileRef,
00193                                                                       const FSRef   *dstDirectoryRef,
00194                                                                       UniCharCount  nameLength, 
00195                                                                       const UniChar        *copyName,           /* can be NULL (no rename during copy) */
00196                                                                       TextEncoding  textEncodingHint,
00197                                                                       FSRef                *newRef);            /* can be NULL */
00198                                                                       
00199 static OSErr         DoCopyFile(   const FSRef          *source,
00200                                                         FSCatalogInfo        *sourceCatInfo,
00201                                                         const FSRef                 *destDir,
00202                                                         const HFSUniStr255   *destName,
00203                                                         CopyParams                  *params, 
00204                                                         FSRef                       *newRef);                   /* can be NULL */
00205 
00206 static OSErr         FSCopyFolder( const FSRef *source,
00207                                                                const FSRef *destDir,
00208                                                                const HFSUniStr255 *destName,             /* can be NULL (no rename during copy) */
00209                                                                CopyParams* copyParams,
00210                                                                FilterParams *filterParams,
00211                                                                ItemCount maxLevels,
00212                                                                FSRef* newDir);                                         /* can be NULL */
00213 
00214 static OSErr         FSCopyFolderLevel( FSCopyObjectGlobals *theGlobals, const HFSUniStr255 *destName );
00215 
00216 static OSErr         CheckForDestInsideSource(   const FSRef *source,
00217                                                                                     const FSRef *destDir);
00218 
00219 static OSErr         CopyItemsForks(      const FSRef *source,
00220                                                                const FSRef *dest,
00221                                                                CopyParams  *params);
00222 
00223 static OSErr         OpenAllForks( const FSRef                 *dest,
00224                                                                const ForkTrackerPtr        dataFork,
00225                                                                const ForkTrackerPtr        rsrcFork,
00226                                                                ForkTrackerPtr                     otherForks,
00227                                                                ItemCount                          otherForksCount);
00228                                                                
00229 static OSErr         CopyFork(     const FSRef                        *source,
00230                                                         const FSRef                 *dest, 
00231                                                         const ForkTrackerPtr        sourceFork,
00232                                                         const CopyParams            *params);
00233                                                         
00234 static OSErr         CloseAllForks(       SInt16                      dataRefNum,
00235                                                                SInt16               rsrcRefNum,
00236                                                                ForkTrackerPtr       otherForks, 
00237                                                                ItemCount            otherForksCount);
00238                                                                
00239 static OSErr         CalculateForksToCopy(       const FSRef                        *source,
00240                                                                              const ForkTrackerPtr dataFork,
00241                                                                              const ForkTrackerPtr rsrcFork,
00242                                                                              ForkTrackerPtr                     *otherForksParam,
00243                                                                              ItemCount                          *otherForksCountParam);
00244                                                                                                                                             
00245 static OSErr         CalculateBufferSize( const FSRef *source,
00246                                                                              const FSRef *destDir,
00247                                                                              ByteCount * bufferSize);
00248 
00249 static ByteCount     BufferSizeForThisVolume(FSVolumeRefNum vRefNum);
00250 
00251 static ByteCount     BufferSizeForThisVolumeSpeed(UInt32 volumeBytesPerSecond);
00252 
00253 static OSErr         IsDropBox(    const FSRef* source,
00254                                                         Boolean *isDropBox);
00255 
00256 static OSErr         GetMagicBusyCreationDate( UTCDateTime *date );
00257 
00258 static Boolean              CompareHFSUniStr255(const HFSUniStr255 *lhs, 
00259                                                                       const HFSUniStr255 *rhs);
00260 
00261 static OSErr         FSGetVRefNum( const FSRef *ref,
00262                                                                FSVolumeRefNum *vRefNum);
00263 
00264 static OSErr         FSGetVolParms(       FSVolumeRefNum volRefNum,
00265                                                                       UInt32 bufferSize,
00266                                                                       GetVolParmsInfoBuffer *volParmsInfo,
00267                                                                       UInt32 *actualInfoSize);    /*     Can Be NULL   */
00268 
00269 static OSErr         UnicodeNameGetHFSName(      UniCharCount nameLength,
00270                                                                                     const UniChar *name,
00271                                                                                     TextEncoding textEncodingHint,
00272                                                                                     Boolean isVolumeName,
00273                                                                                     Str31 hfsName);
00274 
00275 static OSErr         FSMakeFSRef(  FSVolumeRefNum volRefNum,
00276                                                                SInt32 dirID,
00277                                                                ConstStr255Param name,
00278                                                                FSRef *ref);
00279                                                                
00280 static OSErr         FSDeleteFolder( const FSRef *container );
00281 
00282 static void                 FSDeleteFolderLevel( const FSRef *container,
00283                                                                              FSDeleteObjectGlobals *theGlobals);
00284 
00285 /*****************************************************************************/
00286 /*****************************************************************************/
00287 /*****************************************************************************/
00288 
00289 #pragma mark ----- Copy Objects -----
00290 
00291        // This routine acts as the top level of the copy engine.  It exists
00292        // to a) present a nicer API than the various recursive routines, and
00293        // b) minimise the local variables in the recursive routines.
00294 OSErr FSCopyObject(  const FSRef *source,
00295                                    const FSRef *destDir,
00296                                    UniCharCount nameLength,
00297                                    const UniChar *copyName,    // can be NULL (no rename during copy)
00298                                    ItemCount maxLevels,
00299                                    FSCatalogInfoBitmap whichInfo,
00300                                    Boolean wantFSSpec,
00301                                    Boolean wantName,
00302                                    CopyObjectFilterProcPtr filterProcPtr, // can be NULL
00303                                    void *yourDataPtr,          // can be NULL
00304                                    FSRef *newObject)           // can be NULL
00305 {
00306        CopyParams    copyParams;
00307        FilterParams  filterParams;
00308        HFSUniStr255  destName;
00309        HFSUniStr255  *destNamePtr;
00310        Boolean                     isDirectory;
00311        OSErr                osErr = ( source != NULL && destDir != NULL ) ? noErr : paramErr;
00312   
00313        if (copyName)
00314        {
00315               if (nameLength <= 255)
00316               {
00317                      BlockMoveData(copyName, destName.unicode, nameLength * sizeof(UniChar));
00318                      destName.length = nameLength;
00319                      destNamePtr = &destName;
00320               }
00321               else
00322                      osErr = paramErr;
00323        }
00324        else
00325               destNamePtr = NULL;
00326   
00327               // we want the settable info no matter what the user asked for
00328        filterParams.whichInfo             = whichInfo | kFSCatInfoSettableInfo;
00329        filterParams.filterProcPtr  = filterProcPtr;
00330        filterParams.fileSpecPtr    = ( wantFSSpec ) ? &filterParams.fileSpec : NULL;
00331        filterParams.fileNamePtr    = ( wantName   ) ? &filterParams.fileName : NULL;
00332        filterParams.yourDataPtr    = yourDataPtr;
00333 
00334               // Calculate the optimal buffer size to copy the forks over
00335               // and create the buffer
00336        if( osErr == noErr )
00337               osErr = CalculateBufferSize( source, destDir, &copyParams.copyBufferSize);
00338 
00339        if( osErr == noErr )
00340        {
00341               copyParams.copyBuffer = NewPtr( copyParams.copyBufferSize );
00342               if( copyParams.copyBuffer == NULL )
00343                      osErr = memFullErr;
00344        }
00345 
00346        if( osErr == noErr )
00347               osErr = GetMagicBusyCreationDate( &copyParams.magicBusyCreateDate ); 
00348 
00349        if( osErr == noErr ) // figure out if source is a file or folder
00350        {                                         //                     if it is on a local volume, 
00351                                                  //                     if destination is a drop box 
00352               GetVolParmsInfoBuffer       volParms;
00353               FSCatalogInfo               tmpCatInfo;
00354               FSVolumeRefNum                     destVRefNum;
00355 
00356                      // to figure out if the souce is a folder or directory
00357               osErr = FSGetCatalogInfo(source, kFSCatInfoNodeFlags, &tmpCatInfo, NULL, NULL, NULL);
00358               if( osErr == noErr )
00359               {
00360                      isDirectory = ((tmpCatInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0);
00361                             // are we copying to a drop folder?
00362                      osErr = IsDropBox( destDir, &copyParams.copyingToDropFolder );
00363               }
00364               if( osErr == noErr )        
00365                      osErr = FSGetVRefNum(destDir, &destVRefNum);
00366               if( osErr == noErr )
00367                      osErr = FSGetVolParms( destVRefNum, sizeof(volParms), &volParms, NULL );
00368               if( osErr == noErr ) // volParms.vMServerAdr is non-zero for remote volumes
00369                      copyParams.copyingToLocalVolume = (volParms.vMServerAdr == 0);
00370        }
00371        
00372               // now copy the file/folder...
00373        if( osErr == noErr )
00374        {             // is it a folder?
00375               if ( isDirectory )
00376               {             // yes
00377                      osErr = CheckForDestInsideSource(source, destDir);
00378                      if( osErr == noErr )
00379                             osErr = FSCopyFolder( source, destDir, destNamePtr, &copyParams, &filterParams, maxLevels, newObject );
00380               }
00381               else   // no
00382                      osErr = FSCopyFile(source, destDir, destNamePtr, &copyParams, &filterParams, newObject);
00383        }
00384        
00385        // Clean up for space and safety...  Who me?
00386        if( copyParams.copyBuffer != NULL )
00387               DisposePtr((char*)copyParams.copyBuffer);
00388        
00389        mycheck_noerr( osErr );     // put up debug assert in debug builds
00390        
00391        return osErr;
00392 }                            
00393 
00394 /*****************************************************************************/
00395 
00396 #pragma mark ----- Copy Files -----
00397 
00398 OSErr FSCopyFile(    const FSRef          *source,
00399                                    const FSRef          *destDir,
00400                                    const HFSUniStr255 *destName,
00401                                    CopyParams    *copyParams,
00402                                    FilterParams  *filterParams,
00403                                    FSRef                *newFile)
00404 {
00405        FSCatalogInfo        sourceCatInfo;
00406        FSRef                tmpRef;
00407        OSErr                osErr = ( source != NULL && destDir != NULL &&
00408                                                    copyParams != NULL && filterParams != NULL ) ? noErr : paramErr;
00409               
00410               // get needed info about the source file
00411        if ( osErr == noErr )
00412        {
00413               if (destName)
00414               {
00415                      osErr = FSGetCatalogInfo(source, filterParams->whichInfo, &sourceCatInfo, NULL,  NULL, NULL);
00416                      filterParams->fileName = *destName;
00417               }
00418               else
00419                      osErr = FSGetCatalogInfo(source, filterParams->whichInfo, &sourceCatInfo, &filterParams->fileName,  NULL, NULL);
00420        }
00421        if( osErr == noErr )
00422               osErr = CopyFile(source, &sourceCatInfo, destDir, &filterParams->fileName, copyParams, &tmpRef);
00423        
00424               // Call the IterateFilterProc _after_ the new file was created
00425               // even if an error occured
00426        if( filterParams->filterProcPtr != NULL )
00427        {      
00428               (void) CallCopyObjectFilterProc(filterParams->filterProcPtr, false, 0, osErr, &sourceCatInfo,
00429                                                                       &tmpRef, filterParams->fileSpecPtr,
00430                                                                       filterParams->fileNamePtr, filterParams->yourDataPtr);
00431        }
00432        
00433        if( osErr == noErr && newFile != NULL )
00434               *newFile = tmpRef;
00435        
00436        mycheck_noerr(osErr);       // put up debug assert in debug builds
00437 
00438        return osErr;
00439 }
00440 
00441 /*****************************************************************************/
00442 
00443 OSErr CopyFile(      const FSRef *source,
00444                             FSCatalogInfo *sourceCatInfo,
00445                             const FSRef *destDir,
00446                             ConstHFSUniStr255Param destName,
00447                             CopyParams *params,
00448                             FSRef* newFile)
00449 {
00450        OSErr         osErr = paramErr;
00451 
00452               // Clear the "inited" bit so that the Finder positions the icon for us.
00453        ((FInfo *)(sourceCatInfo->finderInfo))->fdFlags &= ~kHasBeenInited;
00454 
00455               // if the destination is on a remote volume, try to use PBHCopyFile
00456        if( params->copyingToLocalVolume == 0 )
00457               osErr = FSUsePBHCopyFile( source, destDir, 0, NULL, kTextEncodingUnknown, newFile );
00458               
00459                                                  // if PBHCopyFile didn't work or not supported,
00460        if( osErr != noErr ) // then try old school file transfer
00461               osErr = DoCopyFile(  source, sourceCatInfo, destDir, destName, params, newFile );          
00462 
00463        mycheck_noerr(osErr);       // put up debug assert in debug builds
00464 
00465        return osErr;
00466 }
00467 
00468 /*****************************************************************************/
00469 
00470 OSErr FSUsePBHCopyFile(     const FSRef *srcFileRef,
00471                                           const FSRef *dstDirectoryRef,
00472                                           UniCharCount nameLength,
00473                                           const UniChar *copyName,    /* can be NULL (no rename during copy) */
00474                                           TextEncoding textEncodingHint,
00475                                           FSRef *newRef)                            /* can be NULL */
00476 {
00477        FSSpec                             srcFileSpec;
00478        FSCatalogInfo               catalogInfo;
00479        GetVolParmsInfoBuffer       volParmsInfo;
00480        HParamBlockRec                     pb;
00481        Str31                              hfsName;
00482        OSErr                              osErr;
00483        
00484                                                         // get source FSSpec from source FSRef
00485        osErr = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL);
00486        if( osErr == noErr )        // Make sure the volume supports CopyFile
00487               osErr = FSGetVolParms(      srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer), &volParmsInfo, NULL);
00488        if( osErr == noErr )
00489               osErr = VolHasCopyFile(&volParmsInfo) ? noErr : paramErr;
00490        if( osErr == noErr )        // get the destination vRefNum and dirID
00491               osErr = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume | kFSCatInfoNodeID, &catalogInfo, NULL, NULL, NULL);
00492        if( osErr == noErr )        // gather all the info needed
00493        {
00494               pb.copyParam.ioVRefNum             = srcFileSpec.vRefNum;
00495               pb.copyParam.ioDirID        = srcFileSpec.parID;
00496               pb.copyParam.ioNamePtr             = (StringPtr)srcFileSpec.name;
00497               pb.copyParam.ioDstVRefNum   = catalogInfo.volume;
00498               pb.copyParam.ioNewDirID            = (long)catalogInfo.nodeID;
00499               pb.copyParam.ioNewName             = NULL;
00500               if( copyName != NULL )
00501                      osErr = UnicodeNameGetHFSName(nameLength, copyName, textEncodingHint, false, hfsName);
00502               pb.copyParam.ioCopyName            = ( copyName != NULL && osErr == noErr ) ? hfsName : NULL;
00503        }
00504        if( osErr == noErr )        // tell the server to copy the object
00505               osErr = PBHCopyFileSync(&pb);
00506        
00507        if( osErr == noErr && newRef != NULL )
00508        {
00509               myverify_noerr(FSMakeFSRef(pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID,
00510                                                          pb.copyParam.ioCopyName, newRef));
00511        }
00512 
00513        if( osErr != paramErr )     // returning paramErr is ok, it means PBHCopyFileSync was not supported
00514               mycheck_noerr(osErr);       // put up debug assert in debug builds
00515 
00516        return osErr;
00517 }
00518 
00519 /*****************************************************************************/
00520 
00521        // Copies a file referenced by source to the directory referenced by
00522        // destDir.  destName is the name the file should be given in the
00523        // destination directory.  sourceCatInfo is the catalogue info of
00524        // the file, which is passed in as an optimization (we could get it
00525        // by doing a FSGetCatalogInfo but the caller has already done that
00526        // so we might as well take advantage of that).
00527        //
00528 OSErr DoCopyFile(    const FSRef *source,
00529                                    FSCatalogInfo *sourceCatInfo,
00530                                    const FSRef *destDir,
00531                                    ConstHFSUniStr255Param destName,
00532                                    CopyParams *params,
00533                                    FSRef *newRef)
00534 {
00535        FSRef                       dest;
00536        FSPermissionInfo     originalPermissions;
00537        UTCDateTime                 originalCreateDate;
00538        OSType                      originalFileType;
00539        UInt16                      originalNodeFlags;
00540        OSErr                       osErr;
00541 
00542               // If we're copying to a drop folder, we won't be able to reset this
00543               // information once the copy is done, so we don't mess it up in
00544               // the first place.  We still clear the locked bit though; items dropped
00545               // into a drop folder always become unlocked.    
00546        if (!params->copyingToDropFolder)
00547        {
00548                      // Remember to clear the file's type, so the Finder doesn't
00549                      // look at the file until we're done.
00550               originalFileType = ((FInfo *) &sourceCatInfo->finderInfo)->fdType;
00551               ((FInfo *) &sourceCatInfo->finderInfo)->fdType = kFirstMagicBusyFiletype;
00552 
00553                      // Remember and clear the file's locked status, so that we can
00554                      // actually write the forks we're about to create.             
00555               originalNodeFlags = sourceCatInfo->nodeFlags;
00556 
00557                      // Set the file's creation date to kMagicBusyCreationDate,
00558                      // remembering the old value for restoration later.
00559               originalCreateDate = sourceCatInfo->createDate;
00560               sourceCatInfo->createDate = params->magicBusyCreateDate;
00561        }
00562        sourceCatInfo->nodeFlags &= ~kFSNodeLockedMask;
00563        
00564               // we need to have user level read/write/execute access to the file we are going to create
00565               // otherwise FSCreateFileUnicode will return -5000 (afpAccessDenied),
00566               // and the FSRef returned will be invalid, yet the file is created (size 0k)... bug?
00567        originalPermissions = *((FSPermissionInfo*)sourceCatInfo->permissions);
00568        ((FSPermissionInfo*)sourceCatInfo->permissions)->mode |= kRWXUserAccessMask;
00569        
00570               // Classic only supports 9.1 and higher, so we don't have to worry about 2397324
00571        osErr = FSCreateFileUnicode(destDir, destName->length, destName->unicode, kFSCatInfoSettableInfo, sourceCatInfo, &dest, NULL);
00572        if( osErr == noErr ) // Copy the forks over to the new file
00573               osErr = CopyItemsForks(source, &dest, params);
00574 
00575               // Restore the original file type, creation and modification dates,
00576               // locked status and permissions.
00577               // This is one of the places where we need to handle drop
00578               // folders as a special case because this FSSetCatalogInfo will fail for
00579               // an item in a drop folder, so we don't even attempt it.             
00580        if (osErr == noErr && !params->copyingToDropFolder)
00581        {
00582               ((FInfo *) &sourceCatInfo->finderInfo)->fdType = originalFileType;
00583               sourceCatInfo->createDate = originalCreateDate;
00584               sourceCatInfo->nodeFlags  = originalNodeFlags;
00585               *((FSPermissionInfo*)sourceCatInfo->permissions) = originalPermissions;
00586 
00587               osErr = FSSetCatalogInfo(&dest, kFSCatInfoSettableInfo, sourceCatInfo);
00588        }
00589        
00590               // If we created the file and the copy failed, try to clean up by
00591               // deleting the file we created.  We do this because, while it's
00592               // possible for the copy to fail halfway through and the File Manager 
00593               // doesn't really clean up that well, we *really* don't wan't
00594               // any half-created files being left around.
00595               // if the file already existed, we don't want to delete it
00596               //
00597               // Note that there are cases where the assert can fire which are not
00598               // errors (for example, if the  destination is in a drop folder) but
00599               // I'll leave it in anyway because I'm interested in discovering those
00600               // cases.  Note that, if this fires and we're running MP, current versions
00601               // of MacsBug won't catch the exception and the MP task will terminate
00602               // with a kMPTaskAbortedErr error.
00603        if (osErr != noErr && osErr != dupFNErr )
00604               myverify_noerr( FSDeleteObjects(&dest) );
00605        else if( newRef != NULL )   // if everything was fine, then return the new file
00606               *newRef = dest;
00607 
00608        mycheck_noerr(osErr);       // put up debug assert in debug builds
00609 
00610        return osErr;
00611 }
00612 
00613 /*****************************************************************************/
00614 
00615 #pragma mark ----- Copy Folders -----
00616 
00617 OSErr FSCopyFolder( const FSRef *source, const FSRef *destDir, const HFSUniStr255 *destName,
00618                     CopyParams* copyParams,  FilterParams *filterParams, ItemCount maxLevels, FSRef* newDir)
00619 {
00620        FSCopyObjectGlobals  theGlobals;
00621 
00622        theGlobals.ref                            = *source;
00623        theGlobals.destRef                 = *destDir;
00624        theGlobals.copyParams              = copyParams;
00625        theGlobals.filterParams            = filterParams;
00626        theGlobals.maxLevels        = maxLevels;
00627        theGlobals.currentLevel            = 0;
00628        theGlobals.quitFlag                = false;
00629        theGlobals.containerChanged = false;
00630        theGlobals.result                  = ( source != NULL && destDir != NULL &&
00631                                                                copyParams != NULL && filterParams != NULL ) ?
00632                                                           noErr : paramErr;
00633        theGlobals.actualObjects    = 0;
00634        
00635               // here we go into recursion land...
00636        if( theGlobals.result == noErr )
00637               theGlobals.result = FSCopyFolderLevel(&theGlobals, destName);
00638 
00639        if( theGlobals.result == noErr && newDir != NULL)
00640               *newDir = theGlobals.ref;
00641 
00642               // Call the IterateFilterProc _after_ the new folder is created
00643               // even if we failed...
00644        if( filterParams->filterProcPtr != NULL )
00645        {      
00646               (void) CallCopyObjectFilterProc(filterParams->filterProcPtr, theGlobals.containerChanged,
00647                                                                       theGlobals.currentLevel, theGlobals.result, &theGlobals.catalogInfo,
00648                                                                       &theGlobals.ref, filterParams->fileSpecPtr,
00649                                                                       filterParams->fileNamePtr, filterParams->yourDataPtr);
00650        }
00651 
00652        mycheck_noerr(theGlobals.result);  // put up debug assert in debug builds
00653 
00654        return ( theGlobals.result );
00655 }
00656 
00657 /*****************************************************************************/
00658 
00659 OSErr FSCopyFolderLevel( FSCopyObjectGlobals *theGlobals, const HFSUniStr255 *destName )
00660 {
00661               // If maxLevels is zero, we aren't checking levels
00662               // If currentLevel < maxLevels, look at this level
00663        if ( (theGlobals->maxLevels == 0) ||
00664                (theGlobals->currentLevel < theGlobals->maxLevels) )
00665        {
00666               FSRef                       newDirRef;
00667               UTCDateTime                 originalCreateDate;
00668               FSPermissionInfo     originalPermissions;
00669               FSIterator                  iterator;
00670               FilterParams         *filterPtr = theGlobals->filterParams;
00671 
00672                      // get the info we need on the source file...
00673               theGlobals->result = FSGetCatalogInfo(    &theGlobals->ref, filterPtr->whichInfo,
00674                                                                                     &theGlobals->catalogInfo, &filterPtr->fileName, 
00675                                                                                     NULL, NULL);
00676 
00677               if (theGlobals->currentLevel == 0 && destName)
00678                      filterPtr->fileName = *destName;
00679 
00680                      // Clear the "inited" bit so that the Finder positions the icon for us.
00681               ((FInfo *)(theGlobals->catalogInfo.finderInfo))->fdFlags &= ~kHasBeenInited;
00682 
00683                      // Set the folder's creation date to kMagicBusyCreationDate
00684                      // so that the Finder doesn't mess with the folder while
00685                      // it's copying.  We remember the old value for restoration
00686                      // later.  We only do this if we're not copying to a drop
00687                      // folder, because if we are copying to a drop folder we don't
00688                      // have the opportunity to reset the information at the end of
00689                      // this routine.
00690               if ( theGlobals->result == noErr && !theGlobals->copyParams->copyingToDropFolder)
00691               {
00692                      originalCreateDate = theGlobals->catalogInfo.createDate;
00693                      theGlobals->catalogInfo.createDate = theGlobals->copyParams->magicBusyCreateDate;
00694               }
00695               
00696                      // we need to have user level read/write/execute access to the folder we are going to create,
00697                      // otherwise FSCreateDirectoryUnicode will return -5000 (afpAccessDenied),
00698                      // and the FSRef returned will be invalid, yet the folder is created...  bug?
00699               originalPermissions = *((FSPermissionInfo*)theGlobals->catalogInfo.permissions);
00700               ((FSPermissionInfo*)theGlobals->catalogInfo.permissions)->mode |= kRWXUserAccessMask;
00701               
00702                      // create the new directory
00703               if( theGlobals->result == noErr )
00704               {
00705                      theGlobals->result = FSCreateDirectoryUnicode(   &theGlobals->destRef, filterPtr->fileName.length,
00706                                                                                                          filterPtr->fileName.unicode, kFSCatInfoSettableInfo,
00707                                                                                                          &theGlobals->catalogInfo, &newDirRef,
00708                                                                                                          &filterPtr->fileSpec, NULL);
00709               }
00710 
00711               ++theGlobals->currentLevel; // setup to go to the next level
00712 
00713                      // With the new APIs, folders can have forks as well as files.  Before
00714                      // we start copying items in the folder, we      must copy over the forks
00715               if( theGlobals->result == noErr )
00716                      theGlobals->result = CopyItemsForks(&theGlobals->ref, &newDirRef, theGlobals->copyParams);
00717               if( theGlobals->result == noErr )  // Open FSIterator for flat access to theGlobals->ref
00718                      theGlobals->result = FSOpenIterator(&theGlobals->ref, kFSIterateFlat, &iterator);
00719               if( theGlobals->result == noErr )
00720               {
00721                      OSErr         osErr;
00722               
00723                             // Call FSGetCatalogInfoBulk in loop to get all items in the container 
00724                      do
00725                      {
00726                             theGlobals->result = FSGetCatalogInfoBulk(       iterator, 1, &theGlobals->actualObjects,
00727                                                                                                          &theGlobals->containerChanged, filterPtr->whichInfo,
00728                                                                                                          &theGlobals->catalogInfo, &theGlobals->ref, 
00729                                                                                                          filterPtr->fileSpecPtr, &filterPtr->fileName);
00730                             if ( ( (theGlobals->result == noErr) || (theGlobals->result == errFSNoMoreItems) ) &&
00731                                     ( theGlobals->actualObjects != 0 ) )
00732                             {
00733                                           // Any errors in here will be passed to the filter proc
00734                                           // we don't want an error in here to prematurely
00735                                           // cancel the recursive copy, leaving a half filled directory
00736                                           
00737                                           // is the new object a directory?
00738                                    if ( (theGlobals->catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0 )
00739                                    {             // yes
00740                                           theGlobals->destRef  = newDirRef;                       
00741                                           osErr = FSCopyFolderLevel(theGlobals, NULL);
00742                                           theGlobals->result = noErr; // don't want one silly mistake to kill the party...
00743                                    }
00744                                    else   // no
00745                                    {
00746                                           osErr = CopyFile(    &theGlobals->ref, &theGlobals->catalogInfo, 
00747                                                                              &newDirRef, &filterPtr->fileName, 
00748                                                                              theGlobals->copyParams, &theGlobals->ref);
00749                                    }
00750 
00751                                           // Call the filter proc _after_ the file/folder was created completly
00752                                    if( filterPtr->filterProcPtr != NULL && !theGlobals->quitFlag )
00753                                    {
00754                                           theGlobals->quitFlag = CallCopyObjectFilterProc(filterPtr->filterProcPtr,
00755                                                                                                   theGlobals->containerChanged, theGlobals->currentLevel,
00756                                                                                                   osErr, &theGlobals->catalogInfo, 
00757                                                                                                   &theGlobals->ref, filterPtr->fileSpecPtr,
00758                                                                                                   filterPtr->fileNamePtr, filterPtr->yourDataPtr);
00759                                    }
00760                             }
00761                      } while ( ( theGlobals->result == noErr ) && ( !theGlobals->quitFlag ) );
00762 
00763                             // Close the FSIterator (closing an open iterator should never fail)
00764                      (void) FSCloseIterator(iterator);
00765               }
00766               
00767                      // errFSNoMoreItems is OK - it only means we hit the end of this level
00768                      // afpAccessDenied is OK, too - it only means we cannot see inside a directory
00769               if ( (theGlobals->result == errFSNoMoreItems) || (theGlobals->result == afpAccessDenied) )
00770                      theGlobals->result = noErr;
00771               
00772                      // store away the name, and an FSSpec and FSRef of the new directory
00773                      // for use in filter proc one level up...
00774               if( theGlobals->result == noErr )
00775               {
00776                      theGlobals->ref             = newDirRef;
00777                      theGlobals->result   = FSGetCatalogInfo(&newDirRef, kFSCatInfoNone, NULL, 
00778                                                                                        &filterPtr->fileName, &filterPtr->fileSpec, NULL);
00779               }
00780               
00781                      // Return to previous level as we leave 
00782               --theGlobals->currentLevel; 
00783               
00784                      // Reset the modification dates and permissions, except when copying to a drop folder
00785                      // where this won't work.
00786               if (theGlobals->result == noErr && ! theGlobals->copyParams->copyingToDropFolder)
00787               {
00788                      theGlobals->catalogInfo.createDate = originalCreateDate;
00789                      *((FSPermissionInfo*)theGlobals->catalogInfo.permissions) = originalPermissions;
00790                      theGlobals->result = FSSetCatalogInfo(&newDirRef,  kFSCatInfoCreateDate
00791                                                                                                           | kFSCatInfoAttrMod 
00792                                                                                                           | kFSCatInfoContentMod
00793                                                                                                           | kFSCatInfoPermissions, &theGlobals->catalogInfo);
00794               }
00795               
00796                      // If we created the folder and the copy failed, try to clean up by
00797                      // deleting the folder we created.  We do this because, while it's
00798                      // possible for the copy to fail halfway through and the File Manager
00799                      // doesn't really clean up that well, we *really* don't wan't any
00800                      // half-created files/folders being left around.
00801                      // if the file already existed, we don't want to delete it
00802               if( theGlobals->result != noErr && theGlobals->result != dupFNErr )
00803                      myverify_noerr( FSDeleteObjects(&newDirRef) );
00804        }
00805 
00806        mycheck_noerr( theGlobals->result );      // put up debug assert in debug builds
00807        
00808        return theGlobals->result;
00809 }
00810 
00811 /*****************************************************************************/
00812 
00813        // Determines whether the destination directory is equal to the source
00814        // item, or whether it's nested inside the source item.  Returns a
00815        // errFSDestInsideSource if that's the case.  We do this to prevent
00816        // endless recursion while copying.
00817        //
00818 OSErr CheckForDestInsideSource(const FSRef *source, const FSRef *destDir)
00819 {
00820        FSRef                thisDir = *destDir;
00821        FSCatalogInfo thisDirInfo;
00822        Boolean                     done = false;
00823        OSErr                osErr;
00824        
00825        do
00826        {
00827               osErr = FSCompareFSRefs(source, &thisDir);
00828               if (osErr == noErr)
00829                      osErr = errFSDestInsideSource;
00830               else if (osErr == diffVolErr)
00831               {
00832                      osErr = noErr;
00833                      done = true;
00834               } 
00835               else if (osErr == errFSRefsDifferent)
00836               {
00837                      // This is somewhat tricky.  We can ask for the parent of thisDir
00838                      // by setting the parentRef parameter to FSGetCatalogInfo but, if
00839                      // thisDir is the volume's FSRef, this will give us back junk.
00840                      // So we also ask for the parent's dir ID to be returned in the
00841                      // FSCatalogInfo record, and then check that against the node
00842                      // ID of the root's parent (ie 1).  If we match that, we've made
00843                      // it to the top of the hierarchy without hitting source, so
00844                      // we leave with no error.
00845                      
00846                      osErr = FSGetCatalogInfo(&thisDir, kFSCatInfoParentDirID, &thisDirInfo, NULL, NULL, &thisDir);
00847                      if( ( osErr == noErr ) && ( thisDirInfo.parentDirID == fsRtParID ) )
00848                             done = true;
00849               }
00850        } while ( osErr == noErr && ! done );
00851        
00852        mycheck_noerr( osErr );     // put up debug assert in debug builds
00853 
00854        return osErr;
00855 }
00856 
00857 /*****************************************************************************/
00858 
00859 #pragma mark ----- Copy Forks -----
00860 
00861 OSErr CopyItemsForks(const FSRef *source, const FSRef *dest, CopyParams *params)
00862 {
00863        ForkTracker          dataFork,
00864                                    rsrcFork;
00865        ForkTrackerPtr       otherForks;
00866        ItemCount            otherForksCount,
00867                                    thisForkIndex;
00868        OSErr                osErr;
00869        
00870        dataFork.forkDestRefNum = 0;
00871        rsrcFork.forkDestRefNum = 0;
00872        otherForks = NULL;
00873        otherForksCount = 0;
00874        
00875               // Get the constant names for the resource and data fork, which
00876               // we're going to need inside the copy engine.
00877        osErr = FSGetDataForkName(&dataFork.forkName);
00878        if( osErr == noErr )
00879               osErr = FSGetResourceForkName(&rsrcFork.forkName);
00880        if( osErr == noErr ) // First determine the list of forks that the source has.
00881               osErr = CalculateForksToCopy(source, &dataFork, &rsrcFork, &otherForks, &otherForksCount);
00882        if (osErr == noErr)
00883        {
00884               // If we're copying into a drop folder, open up all of those forks.
00885               // We have to do this because, once we've starting writing to a fork
00886               // in a drop folder, we can't open any more forks.
00887               //
00888               // We only do this if we're copying into a drop folder in order
00889               // to conserve FCBs in the more common, non-drop folder case.
00890               
00891               if (params->copyingToDropFolder)
00892                      osErr = OpenAllForks(dest, &dataFork, &rsrcFork, otherForks, otherForksCount);
00893        
00894                      // Copy each fork.
00895               if (osErr == noErr && (dataFork.forkSize != 0))         // copy data fork
00896                      osErr = CopyFork(source, dest, &dataFork, params);
00897               if (osErr == noErr && (rsrcFork.forkSize != 0))         // copy resource fork
00898                      osErr = CopyFork(source, dest, &rsrcFork, params);
00899               if (osErr == noErr) {                                                        // copy other forks
00900                      for (thisForkIndex = 0; thisForkIndex < otherForksCount && osErr == noErr; thisForkIndex++)
00901                             osErr = CopyFork(source,dest, &otherForks[thisForkIndex], params);
00902               }
00903 
00904                      // Close any forks that might be left open.  Note that we have to call
00905                      // this regardless of an error.  Also note that this only closes forks
00906                      // that were opened by OpenAllForks.  If we're not copying into a drop
00907                      // folder, the forks are opened and closed by CopyFork.        
00908               {
00909                      OSErr osErr2 = CloseAllForks(dataFork.forkDestRefNum, rsrcFork.forkDestRefNum, otherForks, otherForksCount);
00910                      mycheck_noerr(osErr2);
00911                      if (osErr == noErr)
00912                             osErr = osErr2;
00913               }
00914        }
00915 
00916               // Clean up.  
00917        if (otherForks != NULL)
00918               DisposePtr((char*)otherForks);
00919 
00920        mycheck_noerr( osErr );     // put up debug assert in debug builds
00921 
00922        return osErr;
00923 }
00924 
00925 /*****************************************************************************/
00926 
00927        // Open all the forks of the file.  We need to do this when we're copying
00928        // into a drop folder, where you must open all the forks before starting
00929        // to write to any of them.
00930        //
00931        // IMPORTANT:  If it fails, this routine won't close forks that opened successfully.
00932        // You must call CloseAllForks regardless of whether this routine returns an error.
00933 OSErr OpenAllForks(  const FSRef *dest,
00934                                    const ForkTrackerPtr dataFork,
00935                                    const ForkTrackerPtr rsrcFork,
00936                                    ForkTrackerPtr otherForks,
00937                                    ItemCount otherForksCount)
00938 {
00939        ItemCount     thisForkIndex;
00940        OSErr         osErr                = noErr;
00941 
00942               // Open the resource and data forks as a special case, if they exist in this object
00943        if (dataFork->forkSize != 0)                                          // Data fork never needs to be created, so I don't have to FSCreateFork it here.
00944               osErr = FSOpenFork(dest, dataFork->forkName.length, dataFork->forkName.unicode, fsWrPerm, &dataFork->forkDestRefNum);
00945        if (osErr == noErr && rsrcFork->forkSize != 0)   // Resource fork never needs to be created, so I don't have to FSCreateFork it here.
00946               osErr = FSOpenFork(dest, rsrcFork->forkName.length, rsrcFork->forkName.unicode, fsWrPerm, &rsrcFork->forkDestRefNum);
00947 
00948        if (osErr == noErr && otherForks != NULL && otherForksCount > 0) // Open the other forks.
00949        {
00950               for (thisForkIndex = 0; thisForkIndex < otherForksCount && osErr == noErr; thisForkIndex++)
00951               {
00952                             // Create the fork.  Swallow afpAccessDenied because this operation
00953                             // causes the external file system compatibility shim in Mac OS 9 to
00954                             // generate a GetCatInfo request to the AppleShare external file system,
00955                             // which in turn causes an AFP GetFileDirParms request on the wire,
00956                             // which the AFP server bounces with afpAccessDenied because the file
00957                             // is in a drop folder.  As there's no native support for non-classic
00958                             // forks in current AFP, there's no way I can decide how I should
00959                             // handle this in a non-test case.  So I just swallow the error and
00960                             // hope that when native AFP support arrives, the right thing will happen.
00961                      osErr = FSCreateFork(dest, otherForks[thisForkIndex].forkName.length, otherForks[thisForkIndex].forkName.unicode);
00962                      if (osErr == noErr || osErr == afpAccessDenied)
00963                             osErr = noErr;
00964                      
00965                             // Previously I avoided opening up the fork if the fork if the
00966                             // length was empty, but that confused CopyFork into thinking
00967                             // this wasn't a drop folder copy, so I decided to simply avoid
00968                             // this trivial optimization.  In drop folders, we always open
00969                             // all forks.
00970                      if (osErr == noErr)
00971                             osErr = FSOpenFork(dest, otherForks[thisForkIndex].forkName.length, otherForks[thisForkIndex].forkName.unicode, fsWrPerm, &otherForks[thisForkIndex].forkDestRefNum);
00972               }
00973        }
00974 
00975        mycheck_noerr( osErr );     // put up debug assert in debug builds
00976 
00977        return osErr;
00978 }
00979 
00980 /*****************************************************************************/
00981 
00982        // Copies the fork whose name is forkName from source to dest.
00983        // A refnum for the destination fork may be supplied in forkDestRefNum.
00984        // If forkDestRefNum is 0, we must open the destination fork ourselves,
00985        // otherwise it has been opened for us and we shouldn't close it.
00986 OSErr CopyFork(      const FSRef *source, const FSRef *dest, const ForkTrackerPtr sourceFork, const CopyParams *params)
00987 {
00988        UInt64        bytesRemaining;
00989        UInt64        bytesToReadThisTime;
00990        UInt64        bytesToWriteThisTime;
00991        SInt16        sourceRef;
00992        SInt16        destRef;
00993        OSErr         osErr = noErr;
00994        OSErr         osErr2 = noErr;
00995        
00996               // If we haven't been passed in a sourceFork->forkDestRefNum (which basically
00997               // means we're copying into a non-drop folder), create the destination
00998               // fork.  We have to do this regardless of whether sourceFork->forkSize is 
00999               // 0, because we want to preserve empty forks.
01000        if (sourceFork->forkDestRefNum == 0)
01001        {
01002               osErr = FSCreateFork(dest, sourceFork->forkName.length, sourceFork->forkName.unicode);
01003               
01004                      // Mac OS 9.0 has a bug (in the AppleShare external file system,
01005                      // I think) [2410374] that causes FSCreateFork to return an errFSForkExists
01006                      // error even though the fork is empty.  The following code swallows
01007                      // the error (which is harmless) in that case.
01008               if (osErr == errFSForkExists && !params->copyingToLocalVolume)
01009                      osErr = noErr;
01010        }
01011 
01012        // The remainder of this code only applies if there is actual data
01013        // in the source fork.
01014        
01015        if (osErr == noErr && sourceFork->forkSize != 0) {
01016 
01017               // Prepare for failure.
01018               
01019               sourceRef = 0;
01020               destRef   = 0;
01021 
01022                      // Open up the destination fork, if we're asked to, otherwise
01023                      // just use the passed in sourceFork->forkDestRefNum.   
01024               if( sourceFork->forkDestRefNum == 0 )
01025                      osErr = FSOpenFork(dest, sourceFork->forkName.length, sourceFork->forkName.unicode, fsWrPerm, &destRef);
01026               else
01027                      destRef = sourceFork->forkDestRefNum;
01028               
01029                      // Open up the source fork.
01030               if (osErr == noErr)
01031                      osErr = FSOpenFork(source, sourceFork->forkName.length, sourceFork->forkName.unicode, fsRdPerm, &sourceRef);
01032 
01033                      // Here we create space for the entire fork on the destination volume.
01034                      // FSAllocateFork has the right semantics on both traditional Mac OS
01035                      // and Mac OS X.  On traditional Mac OS it will allocate space for the
01036                      // file in one hit without any other special action.  On Mac OS X,
01037                      // FSAllocateFork is preferable to FSSetForkSize because it prevents
01038                      // the system from zero filling the bytes that were added to the end
01039                      // of the fork (which would be waste becasue we're about to write over
01040                      // those bytes anyway.
01041               if( osErr == noErr )
01042                      osErr = FSAllocateFork(destRef, kFSAllocNoRoundUpMask, fsFromStart, 0, sourceFork->forkSize, NULL);
01043 
01044                      // Copy the file from the source to the destination in chunks of
01045                      // no more than params->copyBufferSize bytes.  This is fairly
01046                      // boring code except for the bytesToReadThisTime/bytesToWriteThisTime
01047                      // distinction.  On the last chunk, we round bytesToWriteThisTime
01048                      // up to the next 512 byte boundary and then, after we exit the loop,
01049                      // we set the file's EOF back to the real location (if the fork size
01050                      // is not a multiple of 512 bytes).
01051                      // 
01052                      // This technique works around a 'bug' in the traditional Mac OS File Manager,
01053                      // where the File Manager will put the last 512-byte block of a large write into
01054                      // the cache (even if we specifically request no caching) if that block is not
01055                      // full. If the block goes into the cache it will eventually have to be
01056                      // flushed, which causes sub-optimal disk performance.
01057                      //
01058                      // This is only done if the destination volume is local.  For a network
01059                      // volume, it's better to just write the last bytes directly.
01060                      //
01061                      // This is extreme over-optimization given the other limits of this
01062                      // sample, but I will hopefully get to the other limits eventually.
01063               bytesRemaining = sourceFork->forkSize;
01064               while (osErr == noErr && bytesRemaining != 0)
01065               {
01066                      if (bytesRemaining > params->copyBufferSize)
01067                      {
01068                             bytesToReadThisTime  =      params->copyBufferSize;
01069                             bytesToWriteThisTime =      bytesToReadThisTime;
01070                      }
01071                      else 
01072                      {
01073                             bytesToReadThisTime  =      bytesRemaining;
01074                             bytesToWriteThisTime =      (params->copyingToLocalVolume)            ?
01075                                                                       (bytesRemaining + 0x01FF) & ~0x01FF :
01076                                                                       bytesRemaining;
01077                      }
01078                      
01079                      osErr = FSReadFork(sourceRef, fsAtMark + noCacheMask, 0, bytesToReadThisTime, params->copyBuffer, NULL);
01080                      if (osErr == noErr)
01081                             osErr = FSWriteFork(destRef, fsAtMark + noCacheMask, 0, bytesToWriteThisTime, params->copyBuffer, NULL);
01082                      if (osErr == noErr)
01083                             bytesRemaining -= bytesToReadThisTime;
01084               }
01085               
01086               if (osErr == noErr && (params->copyingToLocalVolume && ((sourceFork->forkSize & 0x01FF) != 0)) )
01087                      osErr = FSSetForkSize(destRef, fsFromStart, sourceFork->forkSize);
01088 
01089                      // Clean up.
01090               if (sourceRef != 0)
01091               {
01092                      osErr2 = FSCloseFork(sourceRef);
01093                      mycheck_noerr(osErr2);
01094                      if (osErr == noErr)
01095                             osErr = osErr2;
01096               }
01097 
01098                      // Only close destRef if we were asked to open it (ie sourceFork->forkDestRefNum == 0) and
01099                      // we actually managed to open it (ie destRef != 0).    
01100               if (sourceFork->forkDestRefNum == 0 && destRef != 0)
01101               {
01102                      osErr2 = FSCloseFork(destRef);
01103                      mycheck_noerr(osErr2);
01104                      if (osErr == noErr)
01105                             osErr = osErr2;
01106               }
01107        }
01108 
01109        mycheck_noerr( osErr );     // put up debug assert in debug builds
01110 
01111        return osErr;
01112 }
01113 
01114 /*****************************************************************************/
01115 
01116        // Close all the forks that might have been opened by OpenAllForks.
01117 OSErr CloseAllForks(SInt16 dataRefNum, SInt16 rsrcRefNum, ForkTrackerPtr otherForks, ItemCount otherForksCount)
01118 {
01119        ItemCount     thisForkIndex;
01120        OSErr         osErr = noErr,
01121                             osErr2;
01122        
01123        if (dataRefNum != 0) 
01124        {
01125               osErr2 = FSCloseFork(dataRefNum);
01126               mycheck_noerr(osErr2);
01127               if (osErr == noErr)
01128                      osErr = osErr2;
01129        }
01130        if (rsrcRefNum != 0)
01131        {
01132               osErr2 = FSCloseFork(rsrcRefNum);
01133               mycheck_noerr(osErr2);
01134               if (osErr == noErr)
01135                      osErr = osErr2;
01136        }
01137        if( otherForks != NULL && otherForksCount > 0 )
01138        {
01139               for (thisForkIndex = 0; thisForkIndex < otherForksCount; thisForkIndex++)
01140               {
01141                      if (otherForks[thisForkIndex].forkDestRefNum != 0)
01142                      {
01143                             osErr2 = FSCloseFork(otherForks[thisForkIndex].forkDestRefNum);
01144                             mycheck_noerr(osErr2);
01145                             if (osErr == noErr)
01146                                    osErr = osErr2;
01147                      }
01148               }
01149        }
01150        
01151        mycheck_noerr( osErr );     // put up debug assert in debug builds
01152 
01153        return osErr;
01154 }
01155 
01156 /*****************************************************************************/
01157 
01158        // This routine determines the list of forks that a file has.
01159        // dataFork is populated if the file has a data fork.
01160        // rsrcFork is populated if the file has a resource fork.
01161        // otherForksParam is set to point to a memory block allocated with
01162        // NewPtr if the file has forks beyond the resource and data
01163        // forks.  You must free that block with DisposePtr.  otherForksCountParam
01164        // is set to the number of forks in the otherForksParam
01165        // array.  This count does *not* include the resource and data forks.
01166 OSErr CalculateForksToCopy( const FSRef *source,
01167                                                  const ForkTrackerPtr dataFork,
01168                                                  const ForkTrackerPtr rsrcFork,
01169                                                  ForkTrackerPtr *otherForksParam,
01170                                                  ItemCount      *otherForksCountParam)
01171 {
01172        Boolean         done;
01173        CatPositionRec  iterator;
01174        HFSUniStr255  thisForkName;
01175        SInt64          thisForkSize;
01176        ForkTrackerPtr  otherForks;
01177        ItemCount       otherForksCount;
01178        ItemCount       otherForksMemoryBlockCount;
01179        OSErr                osErr = ( (source != NULL)   && (dataFork != NULL) &&
01180                                                    (rsrcFork != NULL) && (otherForksParam != NULL) &&
01181                                                    (otherForksCountParam != NULL) ) ?
01182                                                  noErr : paramErr;
01183 
01184        dataFork->forkSize = 0;
01185        rsrcFork->forkSize = 0;
01186        otherForks = NULL;
01187        otherForksCount = 0;
01188        iterator.initialize = 0;
01189        done = false;
01190 
01191        // Iterate through the list of forks, processing each fork name in turn.
01192        while (osErr == noErr && ! done)
01193        {
01194               osErr = FSIterateForks(source, &iterator, &thisForkName, &thisForkSize, NULL);
01195               if (osErr == errFSNoMoreItems)
01196               {
01197                      osErr = noErr;
01198                      done = true;
01199               }
01200               else if (osErr == noErr)
01201               {
01202                      if ( CompareHFSUniStr255(&thisForkName, &dataFork->forkName) )
01203                             dataFork->forkSize = thisForkSize;
01204                      else if ( CompareHFSUniStr255(&thisForkName, &rsrcFork->forkName) )
01205                             rsrcFork->forkSize = thisForkSize;
01206                      else
01207                      {
01208                                    // We've found a fork other than the resource and data forks.
01209                                    // We have to add it to the otherForks array.  But the array
01210                                    // a) may not have been created yet, and b) may not contain
01211                                    // enough elements to hold the new fork.
01212                                    
01213                             if (otherForks == NULL)     // The array hasn't been allocated yet, allocate it.
01214                             {
01215                                    otherForksMemoryBlockCount = kExpectedForkCount;
01216                                    otherForks = ( ForkTracker* ) NewPtr( sizeof(ForkTracker) * kExpectedForkCount );
01217                                    if (otherForks == NULL)
01218                                           osErr = memFullErr;
01219                             }      
01220                             else if (otherForksCount == otherForksMemoryBlockCount)
01221                             {      // If the array doesn't contain enough elements, grow it.
01222                                    ForkTrackerPtr newOtherForks;
01223                                    
01224                                    newOtherForks = (ForkTracker*)NewPtr(sizeof(ForkTracker) * (otherForksCount + kExpectedForkCount));
01225                                    if( newOtherForks != NULL)
01226                                    {
01227                                           BlockMoveData(otherForks, newOtherForks, sizeof(ForkTracker) * otherForksCount);
01228                                           otherForksMemoryBlockCount += kExpectedForkCount;
01229                                           DisposePtr((char*)otherForks);
01230                                           otherForks = newOtherForks;                             
01231                                    }
01232                                    else
01233                                           osErr = memFullErr;
01234                             }
01235                             
01236                             // If we have no error, we know we have space in the otherForks
01237                             // array to place the new fork.  Put it there and increment the
01238                             // count of forks.
01239                             
01240                             if (osErr == noErr)
01241                             {
01242                                    BlockMoveData(&thisForkName, &otherForks[otherForksCount].forkName, sizeof(thisForkName));
01243                                    otherForks[otherForksCount].forkSize = thisForkSize;
01244                                    otherForks[otherForksCount].forkDestRefNum = 0;
01245                                    ++otherForksCount;
01246                             }
01247                      }
01248               }
01249        }
01250        
01251        // Clean up.
01252        
01253        if (osErr != noErr)
01254        {
01255               if (otherForks != NULL)
01256               {
01257                      DisposePtr((char*)otherForks);
01258                      otherForks = NULL;
01259               }
01260               otherForksCount = 0;
01261        }
01262        
01263        *otherForksParam = otherForks;
01264        *otherForksCountParam = otherForksCount;
01265 
01266        mycheck_noerr( osErr );     // put up debug assert in debug builds
01267 
01268        return osErr; 
01269 }
01270 
01271 /*****************************************************************************/
01272 
01273 #pragma mark ----- Calculate Buffer Size -----
01274 
01275 OSErr CalculateBufferSize( const FSRef *source, const FSRef *destDir,
01276                                              ByteCount * bufferSize )
01277 {
01278        FSVolumeRefNum       sourceVRefNum,
01279                                    destVRefNum;
01280        ByteCount            tmpBufferSize = 0;
01281        OSErr                osErr = ( source != NULL && destDir != NULL && bufferSize != NULL ) ?
01282                                                  noErr : paramErr;
01283        
01284        if( osErr == noErr )
01285               osErr = FSGetVRefNum( source, &sourceVRefNum );
01286        if( osErr == noErr )
01287               osErr = FSGetVRefNum( destDir, &destVRefNum);
01288        if( osErr == noErr)
01289        {
01290               tmpBufferSize = BufferSizeForThisVolume(sourceVRefNum);
01291               if (destVRefNum != sourceVRefNum)
01292               {
01293                      ByteCount tmp = BufferSizeForThisVolume(destVRefNum);
01294                      if (tmp < tmpBufferSize)
01295                             tmpBufferSize = tmp;
01296               }
01297        }
01298        
01299        *bufferSize = tmpBufferSize;
01300 
01301        mycheck_noerr( osErr );     // put up debug assert in debug builds
01302 
01303        return osErr;
01304 }
01305 
01306 /*****************************************************************************/
01307 
01308        // This routine calculates the appropriate buffer size for
01309        // the given vRefNum.  It's a simple composition of FSGetVolParms
01310        // BufferSizeForThisVolumeSpeed.
01311 ByteCount BufferSizeForThisVolume(FSVolumeRefNum vRefNum)
01312 {
01313        GetVolParmsInfoBuffer       volParms;
01314        ByteCount                          volumeBytesPerSecond = 0;
01315        UInt32                             actualSize;
01316        OSErr                              osErr;
01317        
01318        osErr = FSGetVolParms( vRefNum, sizeof(volParms), &volParms, &actualSize );
01319        if( osErr == noErr )
01320        {
01321               // Version 1 of the GetVolParmsInfoBuffer included the vMAttrib
01322               // field, so we don't really need to test actualSize.  A noErr
01323               // result indicates that we have the info we need.  This is
01324               // just a paranoia check.
01325               
01326               mycheck(actualSize >= offsetof(GetVolParmsInfoBuffer, vMVolumeGrade));
01327 
01328               // On the other hand, vMVolumeGrade was not introduced until
01329               // version 2 of the GetVolParmsInfoBuffer, so we have to explicitly
01330               // test whether we got a useful value.
01331               
01332               if( ( actualSize >= offsetof(GetVolParmsInfoBuffer, vMForeignPrivID) ) &&
01333                      ( volParms.vMVolumeGrade <= 0 ) ) 
01334               {
01335                      volumeBytesPerSecond = -volParms.vMVolumeGrade;
01336               }
01337        }
01338 
01339        mycheck_noerr( osErr );     // put up debug assert in debug builds
01340 
01341        return BufferSizeForThisVolumeSpeed(volumeBytesPerSecond);
01342 }
01343 
01344 /*****************************************************************************/
01345 
01346        // Calculate an appropriate copy buffer size based on the volumes
01347        // rated speed.  Our target is to use a buffer that takes 0.25
01348        // seconds to fill.  This is necessary because the volume might be
01349        // mounted over a very slow link (like ARA), and if we do a 256 KB
01350        // read over an ARA link we'll block the File Manager queue for
01351        // so long that other clients (who might have innocently just
01352        // called PBGetCatInfoSync) will block for a noticeable amount of time.
01353        //
01354        // Note that volumeBytesPerSecond might be 0, in which case we assume
01355        // some default value.
01356 ByteCount BufferSizeForThisVolumeSpeed(UInt32 volumeBytesPerSecond)
01357 {
01358        ByteCount bufferSize;
01359        
01360        if (volumeBytesPerSecond == 0)
01361               bufferSize = kDefaultCopyBufferSize;
01362        else
01363        {      // We want to issue a single read that takes 0.25 of a second,
01364               // so devide the bytes per second by 4.
01365               bufferSize = volumeBytesPerSecond / 4;
01366        }
01367        
01368               // Round bufferSize down to 512 byte boundary.
01369        bufferSize &= ~0x01FF;
01370        
01371               // Clip to sensible limits.
01372        if (bufferSize < kMinimumCopyBufferSize)
01373               bufferSize = kMinimumCopyBufferSize;
01374        else if (bufferSize > kMaximumCopyBufferSize)
01375               bufferSize = kMaximumCopyBufferSize;
01376               
01377        return bufferSize;
01378 }
01379 
01380 /*****************************************************************************/
01381 
01382 #pragma mark ----- Delete Objects -----
01383 
01384 OSErr FSDeleteObjects( const FSRef *source )
01385 {
01386        FSCatalogInfo catalogInfo;
01387        OSErr                osErr = ( source != NULL ) ? noErr : paramErr;
01388        
01389               // get nodeFlags for container
01390        if( osErr == noErr )
01391               osErr = FSGetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL,NULL);
01392        if( osErr == noErr && (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0 )
01393        {             // its a directory, so delete its contents before we delete it
01394               osErr = FSDeleteFolder(source);
01395        }
01396        if( osErr == noErr && (catalogInfo.nodeFlags & kFSNodeLockedMask) != 0 )     // is object locked?
01397        {             // then attempt to unlock the object (ignore osErr since FSDeleteObject will set it correctly)
01398               catalogInfo.nodeFlags &= ~kFSNodeLockedMask;
01399               (void) FSSetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo);
01400        }             
01401        if( osErr == noErr ) // delete the object (if it was a directory it is now empty, so we can delete it)
01402               osErr = FSDeleteObject(source);
01403 
01404        mycheck_noerr( osErr );
01405        
01406        return ( osErr );
01407 }
01408 
01409 /*****************************************************************************/
01410 
01411 #pragma mark ----- Delete Folders -----
01412 
01413 OSErr FSDeleteFolder( const FSRef *container )
01414 {
01415        FSDeleteObjectGlobals       theGlobals;
01416        
01417        theGlobals.result = ( container != NULL ) ? noErr : paramErr;
01418        
01419               // delete container's contents
01420        if( theGlobals.result == noErr )
01421               FSDeleteFolderLevel(container, &theGlobals);
01422        
01423        mycheck_noerr( theGlobals.result );
01424        
01425        return ( theGlobals.result );
01426 }
01427 
01428 /*****************************************************************************/
01429 
01430 void FSDeleteFolderLevel(   const FSRef *container,
01431                                                         FSDeleteObjectGlobals *theGlobals)
01432 {
01433        FSIterator                                iterator;
01434        FSRef                                     itemToDelete;
01435        UInt16                                    nodeFlags;
01436 
01437               // Open FSIterator for flat access and give delete optimization hint
01438        theGlobals->result = FSOpenIterator(container, kFSIterateFlat + kFSIterateDelete, &iterator);
01439        if ( theGlobals->result == noErr )
01440        {
01441               do     // delete the contents of the directory
01442               {
01443                             // get 1 item to delete
01444                      theGlobals->result = FSGetCatalogInfoBulk(       iterator, 1, &theGlobals->actualObjects,
01445                                                                                                   NULL, kFSCatInfoNodeFlags, &theGlobals->catalogInfo,
01446                                                                                                   &itemToDelete, NULL, NULL);
01447                      if ( (theGlobals->result == noErr) && (theGlobals->actualObjects == 1) )
01448                      {
01449                                    // save node flags in local in case we have to recurse */
01450                             nodeFlags = theGlobals->catalogInfo.nodeFlags;
01451                             
01452                                    // is it a directory?
01453                             if ( (nodeFlags & kFSNodeIsDirectoryMask) != 0 )
01454                             {      // yes -- delete its contents before attempting to delete it */ 
01455                                    FSDeleteFolderLevel(&itemToDelete, theGlobals);
01456                             }
01457                             if ( theGlobals->result == noErr)                // are we still OK to delete?
01458                             {      
01459                                    if ( (nodeFlags & kFSNodeLockedMask) != 0 )      // is item locked?
01460                                    {             // then attempt to unlock it (ignore result since FSDeleteObject will set it correctly)
01461                                           theGlobals->catalogInfo.nodeFlags = nodeFlags & ~kFSNodeLockedMask;
01462                                           (void) FSSetCatalogInfo(&itemToDelete, kFSCatInfoNodeFlags, &theGlobals->catalogInfo);
01463                                    }
01464                                           // delete the item
01465                                    theGlobals->result = FSDeleteObject(&itemToDelete);
01466                             }
01467                      }
01468               } while ( theGlobals->result == noErr );
01469                      
01470                      // we found the end of the items normally, so return noErr
01471               if ( theGlobals->result ==  errFSNoMoreItems )
01472                      theGlobals->result = noErr;
01473                      
01474                      // close the FSIterator (closing an open iterator should never fail)
01475               myverify_noerr(FSCloseIterator(iterator));
01476        }
01477 
01478        mycheck_noerr( theGlobals->result );
01479        
01480        return;
01481 }
01482 
01483 /*****************************************************************************/
01484 
01485 #pragma mark ----- Utilities -----
01486 
01487        // Figures out if the given directory is a drop box or not
01488        // if it is, the Copy Engine will behave slightly differently
01489 OSErr  IsDropBox(const FSRef* source, Boolean *isDropBox)
01490 {
01491        FSCatalogInfo               tmpCatInfo;
01492        FSSpec                             sourceSpec;
01493        Boolean                                   isDrop = false;
01494        OSErr                              osErr;
01495        
01496               // get info about the destination, and an FSSpec to it for PBHGetDirAccess
01497        osErr = FSGetCatalogInfo(source, kFSCatInfoNodeFlags | kFSCatInfoPermissions, &tmpCatInfo, NULL, &sourceSpec, NULL);
01498        if( osErr == noErr ) // make sure the source is a directory
01499               osErr = ((tmpCatInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) ? noErr : errFSNotAFolder;
01500        if( osErr == noErr )
01501        {
01502               HParamBlockRec       hPB;
01503 
01504               BlockZero(&hPB, sizeof( HParamBlockRec ));
01505 
01506               hPB.accessParam.ioNamePtr          = sourceSpec.name;
01507               hPB.accessParam.ioVRefNum          = sourceSpec.vRefNum;
01508               hPB.accessParam.ioDirID                   = sourceSpec.parID;
01509               
01510                      // This is the official way (reads: the way X Finder does it) to figure
01511                      // out the current users access privileges to a given directory
01512               osErr = PBHGetDirAccessSync(&hPB);
01513               if( osErr == noErr ) // its a drop folder if the current user only has write access
01514                      isDrop = (hPB.accessParam.ioACAccess & kPrivilegesMask) == kioACAccessUserWriteMask;
01515               else if ( osErr == paramErr )
01516               {
01517                      // There is a bug (2908703) in the Classic File System (not OS 9.x or Carbon)
01518                      // on 10.1.x where PBHGetDirAccessSync sometimes returns paramErr even when the
01519                      // data passed in is correct.  This is a workaround/hack for that problem,
01520                      // but is not as accurate.
01521                      // Basically, if "Everyone" has only Write/Search access then its a drop folder
01522                      // that is the most common case when its a drop folder
01523                      FSPermissionInfo *tmpPerm = (FSPermissionInfo *)tmpCatInfo.permissions;                    
01524                      isDrop = ((tmpPerm->mode & kRWXOtherAccessMask) == kDropFolderValue);
01525                      osErr = noErr;
01526               }
01527        }
01528 
01529        *isDropBox = isDrop;
01530 
01531        mycheck_noerr( osErr );
01532        
01533        return osErr;
01534 }
01535 
01536        // The copy engine is going to set each item's creation date
01537        // to kMagicBusyCreationDate while it's copying the item.
01538        // But kMagicBusyCreationDate is an old-style 32-bit date/time,
01539        // while the HFS Plus APIs use the new 64-bit date/time.  So
01540        // we have to call a happy UTC utilities routine to convert from
01541        // the local time kMagicBusyCreationDate to a UTCDateTime
01542        // gMagicBusyCreationDate, which the File Manager will store
01543        // on disk and which the Finder we read back using the old
01544        // APIs, whereupon the File Manager will convert it back
01545        // to local time (and hopefully get the kMagicBusyCreationDate
01546        // back!).
01547 OSErr  GetMagicBusyCreationDate( UTCDateTime *date )
01548 {
01549        UTCDateTime          tmpDate = {0,0,0};
01550        OSErr                osErr = ( date != NULL ) ? noErr : paramErr;
01551                             
01552        if( osErr == noErr )
01553               osErr = ConvertLocalTimeToUTC(kMagicBusyCreationDate, &tmpDate.lowSeconds);
01554        if( osErr == noErr )
01555               *date = tmpDate;
01556               
01557        mycheck_noerr( osErr );     // put up debug assert in debug builds
01558 
01559        return osErr;
01560 }
01561 
01562        // compares two HFSUniStr255 for equality
01563        // return true if they are identical, false if not
01564 Boolean CompareHFSUniStr255(const HFSUniStr255 *lhs, const HFSUniStr255 *rhs)
01565 {
01566        return (lhs->length == rhs->length)
01567                      && (memcmp(lhs->unicode, rhs->unicode, lhs->length * sizeof(UniChar)) == 0);
01568 }
01569 
01570 /*****************************************************************************/
01571 
01572 OSErr FSGetVRefNum(const FSRef *ref, FSVolumeRefNum *vRefNum)
01573 {
01574        FSCatalogInfo catalogInfo;
01575        OSErr                osErr = ( ref != NULL && vRefNum != NULL ) ? noErr : paramErr;
01576 
01577        if( osErr == noErr ) /* get the volume refNum from the FSRef */
01578               osErr = FSGetCatalogInfo(ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
01579        if( osErr == noErr )
01580               *vRefNum = catalogInfo.volume;
01581               
01582        mycheck_noerr( osErr );
01583 
01584        return osErr;
01585 }
01586 
01587 /*****************************************************************************/ 
01588 
01589 OSErr FSGetVolParms( FSVolumeRefNum volRefNum,
01590                                           UInt32 bufferSize,
01591                                           GetVolParmsInfoBuffer *volParmsInfo,
01592                                           UInt32 *actualInfoSize)            /*     Can Be NULL   */
01593 {
01594        HParamBlockRec       pb;
01595        OSErr                osErr = ( volParmsInfo != NULL ) ? noErr : paramErr;
01596               
01597        if( osErr == noErr )
01598        {
01599               pb.ioParam.ioNamePtr = NULL;
01600               pb.ioParam.ioVRefNum = volRefNum;
01601               pb.ioParam.ioBuffer = (Ptr)volParmsInfo;
01602               pb.ioParam.ioReqCount = (SInt32)bufferSize;
01603               osErr = PBHGetVolParmsSync(&pb);
01604        }
01605               /* return number of bytes the file system returned in volParmsInfo buffer */
01606        if( osErr == noErr && actualInfoSize != NULL)
01607               *actualInfoSize = (UInt32)pb.ioParam.ioActCount;
01608 
01609        mycheck_noerr( osErr );     // put up debug assert in debug builds
01610 
01611        return ( osErr );
01612 }
01613 
01614 /*****************************************************************************/
01615 
01616 OSErr UnicodeNameGetHFSName(       UniCharCount nameLength,
01617                                                         const UniChar *name,
01618                                                         TextEncoding textEncodingHint,
01619                                                         Boolean isVolumeName,
01620                                                         Str31 hfsName)
01621 {
01622        UnicodeMapping              uMapping;
01623        UnicodeToTextInfo    utInfo;
01624        ByteCount                   unicodeByteLength;
01625        ByteCount                   unicodeBytesConverted;
01626        ByteCount                   actualPascalBytes;
01627        OSErr                       osErr = (hfsName != NULL && name != NULL ) ? noErr : paramErr;
01628               
01629               // make sure output is valid in case we get errors or there's nothing to convert
01630        hfsName[0] = 0;
01631        
01632        unicodeByteLength = nameLength * sizeof(UniChar);
01633        if ( unicodeByteLength == 0 )      
01634               osErr = noErr;              /* do nothing */
01635        else
01636        {
01637                      // if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint
01638               if ( kTextEncodingUnknown == textEncodingHint )
01639               {
01640                      ScriptCode                  script;
01641                      RegionCode                  region;
01642                      
01643                      script = (ScriptCode)GetScriptManagerVariable(smSysScript);
01644                      region = (RegionCode)GetScriptManagerVariable(smRegionCode);
01645                      osErr = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, 
01646                                                                                            region, NULL, &textEncodingHint );
01647                      if ( osErr == paramErr )
01648                      {             // ok, ignore the region and try again
01649                             osErr = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare,
01650                                                                                                   kTextRegionDontCare, NULL, 
01651                                                                                                   &textEncodingHint );
01652                      }
01653                      if ( osErr != noErr )                     // ok... try something
01654                             textEncodingHint = kTextEncodingMacRoman;
01655               }
01656               
01657               uMapping.unicodeEncoding    = CreateTextEncoding(       kTextEncodingUnicodeV2_0,
01658                                                                                                          kUnicodeCanonicalDecompVariant, 
01659                                                                                                          kUnicode16BitFormat);
01660               uMapping.otherEncoding             = GetTextEncodingBase(textEncodingHint);
01661               uMapping.mappingVersion            = kUnicodeUseHFSPlusMapping;
01662        
01663               osErr = CreateUnicodeToTextInfo(&uMapping, &utInfo);
01664               if( osErr == noErr )
01665               {
01666                      osErr = ConvertFromUnicodeToText(  utInfo, unicodeByteLength, name, kUnicodeLooseMappingsMask,
01667                                                                                     0, NULL, 0, NULL,    /* offsetCounts & offsetArrays */
01668                                                                                     isVolumeName ? kHFSMaxVolumeNameChars : kHFSMaxFileNameChars,
01669                                                                                     &unicodeBytesConverted, &actualPascalBytes, &hfsName[1]);
01670               }
01671               if( osErr == noErr )
01672                      hfsName[0] = actualPascalBytes;
01673               
01674                      // verify the result in debug builds -- there's really not anything you can do if it fails
01675               myverify_noerr(DisposeUnicodeToTextInfo(&utInfo));
01676        }
01677 
01678        mycheck_noerr( osErr );     // put up debug assert in debug builds
01679        
01680        return ( osErr );
01681 }
01682 
01683 /*****************************************************************************/
01684 
01685 OSErr FSMakeFSRef(   FSVolumeRefNum volRefNum,
01686                                    SInt32 dirID,
01687                                    ConstStr255Param name,
01688                                    FSRef *ref)
01689 {
01690        FSRefParam    pb;
01691        OSErr         osErr = ( ref != NULL ) ? noErr : paramErr;
01692        
01693        if( osErr == noErr )
01694        {
01695               pb.ioVRefNum = volRefNum;
01696               pb.ioDirID = dirID;
01697               pb.ioNamePtr = (StringPtr)name;
01698               pb.newRef = ref;
01699               osErr = PBMakeFSRefSync(&pb);
01700        }
01701        
01702        mycheck_noerr( osErr );     // put up debug assert in debug builds
01703               
01704        return ( osErr );
01705 }