4         Contains:       A robust, general purpose file copy routine. 
   8         Copyright:      © 1992-2001 by Apple Computer, Inc., all rights reserved. 
  10         You may incorporate this sample code into your applications without 
  11         restriction, though the sample code has been provided "AS IS" and the 
  12         responsibility for its operation is 100% yours.  However, what you are 
  13         not permitted to do is to redistribute the source as "DSC Sample Code" 
  14         after having made changes. If you're going to re-distribute the source, 
  15         we require that you make it clear in the source that the code was 
  16         descended from Apple Sample Code, but that you've made changes. 
  20                 DRI:                            Apple Macintosh Developer Technical Support 
  22                 Other Contact:          Apple Macintosh Developer Technical Support 
  23                                                         <http://developer.apple.com/bugreporter/> 
  25                 Technology:                     DTS Sample Code 
  31         Change History (most recent first): 
  33                  <2>      2/7/01        JL              Added standard header. Updated names of includes. Updated 
  34                                                                         various routines to use new calling convention of the 
  35                                                                         MoreFilesExtras accessor functions. 
  36                 <1>             12/06/99        JL              MoreFiles 1.5. 
  40 #include <MacErrors.h> 
  41 #include <MacMemory.h> 
  45 #define __COMPILINGMOREFILES 
  47 #include "MoreFiles.h" 
  48 #include "MoreFilesExtras.h" 
  49 #include "MoreDesktopMgr.h" 
  52 /*****************************************************************************/ 
  56 /*      The deny-mode privileges to use when opening the source and destination files. */ 
  60         srcCopyMode 
= dmRdDenyWr
, 
  61         dstCopyMode 
= dmWrDenyRdWr
 
  64 /*      The largest (16K) and smallest (.5K) copy buffer to use if the caller doesn't supply  
  65 **      their own copy buffer. */ 
  69         bigCopyBuffSize  
= 0x00004000, 
  70         minCopyBuffSize  
= 0x00000200 
  73 /*****************************************************************************/ 
  75 /* static prototypes */ 
  77 static  OSErr   
GetDestinationDirInfo(short vRefNum
, 
  79                                                                           ConstStr255Param name
, 
  83 /*      GetDestinationDirInfo tells us if the destination is a directory, it's 
  84         directory ID, and if it's an AppleShare drop box (write privileges only -- 
  85         no read or search privileges). 
  86         vRefNum         input:  Volume specification. 
  87         dirID           input:  Directory ID. 
  88         name            input:  Pointer to object name, or nil when dirID 
  89                                                 specifies a directory that's the object. 
  90         theDirID        output: If the object is a file, then its parent directory 
  91                                                 ID. If the object is a directory, then its ID. 
  92         isDirectory     output: True if object is a directory; false if 
  94         isDropBox       output: True if directory is an AppleShare drop box. 
  97 static  OSErr   
CheckForForks(short vRefNum
, 
  99                                                           ConstStr255Param name
, 
 100                                                           Boolean 
*hasDataFork
, 
 101                                                           Boolean 
*hasResourceFork
); 
 102 /*      CheckForForks tells us if there is a data or resource fork to copy. 
 103         vRefNum         input:  Volume specification of the file's current 
 105         dirID           input:  Directory ID of the file's current location. 
 106         name            input:  The name of the file. 
 109 static  OSErr   
PreflightFileCopySpace(short srcVRefNum
, 
 111                                                                            ConstStr255Param srcName
, 
 112                                                                            ConstStr255Param dstVolName
, 
 115 /*      PreflightFileCopySpace determines if there's enough space on a 
 116         volume to copy the specified file to that volume. 
 117         Note: The results of this routine are not perfect. For example if the 
 118         volume's catalog or extents overflow file grows when the new file is 
 119         created, more allocation blocks may be needed beyond those needed for 
 120         the file's data and resource forks. 
 122         srcVRefNum              input:  Volume specification of the file's current 
 124         srcDirID                input:  Directory ID of the file's current location. 
 125         srcName                 input:  The name of the file. 
 126         dstVolName              input:  A pointer to the name of the volume where 
 127                                                         the file will be copied or NULL. 
 128         dstVRefNum              input:  Volume specification indicating the volume 
 129                                                         where the file will be copied. 
 130         spaceOK                 output: true if there's enough space on the volume for 
 131                                                         the file's data and resource forks. 
 134 /*****************************************************************************/ 
 136 static  OSErr   
GetDestinationDirInfo(short vRefNum
, 
 138                                                                           ConstStr255Param name
, 
 140                                                                           Boolean 
*isDirectory
, 
 146         pb
.dirInfo
.ioACUser 
= 0;        /* ioACUser used to be filler2, clear it before calling GetCatInfo */ 
 147         error 
= GetCatInfoNoName(vRefNum
, dirID
, name
, &pb
); 
 148         *theDirID 
= pb
.dirInfo
.ioDrDirID
; 
 149         *isDirectory 
= (pb
.dirInfo
.ioFlAttrib 
& kioFlAttribDirMask
) != 0; 
 150         /* see if access priviledges are make changes, not see folder, and not see files (drop box) */ 
 151         *isDropBox 
= userHasDropBoxAccess(pb
.dirInfo
.ioACUser
); 
 156 /*****************************************************************************/ 
 158 static  OSErr   
CheckForForks(short vRefNum
, 
 160                                                           ConstStr255Param name
, 
 161                                                           Boolean 
*hasDataFork
, 
 162                                                           Boolean 
*hasResourceFork
) 
 167         pb
.fileParam
.ioNamePtr 
= (StringPtr
)name
; 
 168         pb
.fileParam
.ioVRefNum 
= vRefNum
; 
 169         pb
.fileParam
.ioFVersNum 
= 0; 
 170         pb
.fileParam
.ioDirID 
= dirID
; 
 171         pb
.fileParam
.ioFDirIndex 
= 0; 
 172         error 
= PBHGetFInfoSync(&pb
); 
 173         *hasDataFork 
= (pb
.fileParam
.ioFlLgLen 
!= 0); 
 174         *hasResourceFork 
= (pb
.fileParam
.ioFlRLgLen 
!= 0); 
 179 /*****************************************************************************/ 
 181 static  OSErr   
PreflightFileCopySpace(short srcVRefNum
, 
 183                                                                            ConstStr255Param srcName
, 
 184                                                                            ConstStr255Param dstVolName
, 
 190         unsigned long dstFreeBlocks
; 
 191         unsigned long dstBlksPerAllocBlk
; 
 192         unsigned long srcDataBlks
; 
 193         unsigned long srcResourceBlks
; 
 195         error 
= XGetVolumeInfoNoName(dstVolName
, dstVRefNum
, &pb
.xPB
); 
 196         if ( error 
== noErr 
) 
 198                 /* get allocation block size (always multiple of 512) and divide by 512 
 199                   to get number of 512-byte blocks per allocation block */ 
 200                 dstBlksPerAllocBlk 
= ((unsigned long)pb
.xPB
.ioVAlBlkSiz 
>> 9); 
 202                 /* Convert freeBytes to free disk blocks (512-byte blocks) */ 
 203                 dstFreeBlocks 
= U32SetU(U64ShiftRight(pb
.xPB
.ioVFreeBytes
, 9)); 
 205                 /* Now, get the size of the file's data resource forks */ 
 206                 pb
.hPB
.fileParam
.ioNamePtr 
= (StringPtr
)srcName
; 
 207                 pb
.hPB
.fileParam
.ioVRefNum 
= srcVRefNum
; 
 208                 pb
.hPB
.fileParam
.ioFVersNum 
= 0; 
 209                 pb
.hPB
.fileParam
.ioDirID 
= srcDirID
; 
 210                 pb
.hPB
.fileParam
.ioFDirIndex 
= 0; 
 211                 error 
= PBHGetFInfoSync(&pb
.hPB
); 
 212                 if ( error 
== noErr 
) 
 214                         /* Since space on Mac OS disks is always allocated in allocation blocks, */ 
 215                         /* this code takes into account rounding up to the end of an allocation block. */ 
 217                         /* get number of 512-byte blocks needed for data fork */ 
 218                         if ( ((unsigned long)pb
.hPB
.fileParam
.ioFlLgLen 
& 0x000001ff) != 0 ) 
 220                                 srcDataBlks 
= ((unsigned long)pb
.hPB
.fileParam
.ioFlLgLen 
>> 9) + 1; 
 224                                 srcDataBlks 
= (unsigned long)pb
.hPB
.fileParam
.ioFlLgLen 
>> 9; 
 227                         /* now, calculate number of new allocation blocks needed */ 
 228                         if ( srcDataBlks 
% dstBlksPerAllocBlk 
) 
 230                                 srcDataBlks 
= (srcDataBlks 
/ dstBlksPerAllocBlk
) + 1; 
 234                                 srcDataBlks 
/= dstBlksPerAllocBlk
; 
 237                         /* get number of 512-byte blocks needed for resource fork */ 
 238                         if ( ((unsigned long)pb
.hPB
.fileParam
.ioFlRLgLen 
& 0x000001ff) != 0 ) 
 240                                 srcResourceBlks 
= ((unsigned long)pb
.hPB
.fileParam
.ioFlRLgLen 
>> 9) + 1; 
 244                                 srcResourceBlks 
= (unsigned long)pb
.hPB
.fileParam
.ioFlRLgLen 
>> 9; 
 247                         /* now, calculate number of new allocation blocks needed */ 
 248                         if ( srcResourceBlks 
% dstBlksPerAllocBlk 
) 
 250                                 srcResourceBlks 
= (srcResourceBlks 
/ dstBlksPerAllocBlk
) + 1; 
 254                                 srcResourceBlks 
/= dstBlksPerAllocBlk
; 
 257                         /* Is there enough room on the destination volume for the source file? */ 
 258                         *spaceOK 
= ( ((srcDataBlks 
+ srcResourceBlks
) * dstBlksPerAllocBlk
) <= dstFreeBlocks 
); 
 265 /*****************************************************************************/ 
 267 pascal  OSErr   
FileCopy(short srcVRefNum
, 
 269                                                  ConstStr255Param srcName
, 
 272                                                  ConstStr255Param dstPathname
, 
 273                                                  ConstStr255Param copyName
, 
 280         short   srcRefNum 
= 0,                  /* 0 when source data and resource fork are closed  */ 
 281                         dstDataRefNum 
= 0,              /* 0 when destination data fork is closed */ 
 282                         dstRsrcRefNum 
= 0;              /* 0 when destination resource fork is closed */ 
 284         Str63   dstName
;                                /* The filename of the destination. It might be the 
 285                                                                         ** source filename, it might be a new name... */ 
 287         GetVolParmsInfoBuffer infoBuffer
; /* Where PBGetVolParms dumps its info */ 
 288         long    srcServerAdr
;                   /* AppleTalk server address of source (if any) */ 
 290         Boolean dstCreated 
= false,             /* true when destination file has been created */ 
 291                         ourCopyBuffer 
= false,  /* true if we had to allocate the copy buffer */ 
 292                         isDirectory
,                    /* true if destination is really a directory */ 
 293                         isDropBox
;                              /* true if destination is an AppleShare drop box */ 
 298         Boolean spaceOK
;                                /* true if there's enough room to copy the file to the destination volume */ 
 301         Boolean hasResourceFork
; 
 303         /* Preflight for size */ 
 306                 err 
= PreflightFileCopySpace(srcVRefNum
, srcDirID
, srcName
, 
 307                                                                          dstPathname
, dstVRefNum
, &spaceOK
); 
 315                         return ( dskFulErr 
); 
 319         /* get the destination's real dirID and make sure it really is a directory */ 
 320         err 
= GetDestinationDirInfo(dstVRefNum
, dstDirID
, dstPathname
, 
 321                                                                 &dstDirID
, &isDirectory
, &isDropBox
); 
 332         /* get the destination's real vRefNum */ 
 333         err 
= DetermineVRefNum(dstPathname
, dstVRefNum
, &dstVRefNum
); 
 339         /* See if PBHCopyFile can be used.  Using PBHCopyFile saves time by letting the file server 
 340         ** copy the file if the source and destination locations are on the same file server. */ 
 341         tempLong 
= sizeof(infoBuffer
); 
 342         err 
= HGetVolParms(srcName
, srcVRefNum
, &infoBuffer
, &tempLong
); 
 343         if ( (err 
!= noErr
) && (err 
!= paramErr
) ) 
 348         if ( (err 
!= paramErr
) && hasCopyFile(&infoBuffer
) ) 
 350                 /* The source volume supports PBHCopyFile. */ 
 351                 srcServerAdr 
= infoBuffer
.vMServerAdr
; 
 353                 /* Now, see if the destination volume is on the same file server. */ 
 354                 tempLong 
= sizeof(infoBuffer
); 
 355                 err 
= HGetVolParms(NULL
, dstVRefNum
, &infoBuffer
, &tempLong
); 
 356                 if ( (err 
!= noErr
) && (err 
!= paramErr
) ) 
 360                 if ( (err 
!= paramErr
) && (srcServerAdr 
== infoBuffer
.vMServerAdr
) ) 
 362                         /* Source and Dest are on same server and PBHCopyFile is supported. Copy with CopyFile. */ 
 363                         err 
= HCopyFile(srcVRefNum
, srcDirID
, srcName
, dstVRefNum
, dstDirID
, NULL
, copyName
); 
 369                         /* AppleShare's CopyFile clears the isAlias bit, so I still need to attempt to copy 
 370                            the File's attributes to attempt to get things right. */ 
 371                         if ( copyName 
!= NULL 
)                         /* Did caller supply copy file name? */ 
 373                                 /* Yes, use the caller supplied copy file name. */ 
 374                                 (void) CopyFileMgrAttributes(srcVRefNum
, srcDirID
, srcName
, 
 375                                                                                          dstVRefNum
, dstDirID
, copyName
, true); 
 379                                 /* They didn't, so get the source file name and use it. */ 
 380                                 if ( GetFilenameFromPathname(srcName
, dstName
) == noErr 
) 
 383                                         (void) CopyFileMgrAttributes(srcVRefNum
, srcDirID
, srcName
, 
 384                                                                                                  dstVRefNum
, dstDirID
, dstName
, true); 
 391         /* If we're here, then PBHCopyFile couldn't be used so we have to copy the file by hand. */ 
 393         /* Make sure a copy buffer is allocated. */ 
 394         if ( copyBufferPtr 
== NULL 
) 
 396                 /* The caller didn't supply a copy buffer so grab one from the application heap. 
 397                 ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer. 
 398                 ** If 512 bytes aren't available, we're in trouble. */ 
 399                 copyBufferSize 
= bigCopyBuffSize
; 
 400                 copyBufferPtr 
= NewPtr(copyBufferSize
); 
 401                 if ( copyBufferPtr 
== NULL 
) 
 403                         copyBufferSize 
= minCopyBuffSize
; 
 404                         copyBufferPtr 
= NewPtr(copyBufferSize
); 
 405                         if ( copyBufferPtr 
== NULL 
) 
 407                                 return ( memFullErr 
); 
 410                 ourCopyBuffer 
= true; 
 413         /* Open the source data fork. */ 
 414         err 
= HOpenAware(srcVRefNum
, srcDirID
, srcName
, srcCopyMode
, &srcRefNum
); 
 418         /* Once a file is opened, we have to exit via ErrorExit to make sure things are cleaned up */ 
 420         /* See if the copy will be renamed. */ 
 421         if ( copyName 
!= NULL 
)                         /* Did caller supply copy file name? */ 
 422                 BlockMoveData(copyName
, dstName
, copyName
[0] + 1);      /* Yes, use the caller supplied copy file name. */ 
 424         {       /* They didn't, so get the source file name and use it. */ 
 425                 err 
= GetFileLocation(srcRefNum
, &tempInt
, &tempLong
, dstName
); 
 432         /* Create the destination file. */ 
 433         err 
= HCreateMinimum(dstVRefNum
, dstDirID
, dstName
); 
 438         dstCreated 
= true;      /* After creating the destination file, any 
 439                                                 ** error conditions should delete the destination file */ 
 441         /* An AppleShare dropbox folder is a folder for which the user has the Make Changes 
 442         ** privilege (write access), but not See Files (read access) and See Folders (search access). 
 443         ** Copying a file into an AppleShare dropbox presents some special problems. Here are the 
 444         ** rules we have to follow to copy a file into a dropbox: 
 445         ** ¥ File attributes can be changed only when both forks of a file are empty. 
 446         ** ¥ DeskTop Manager comments can be added to a file only when both forks of a file  
 448         ** ¥ A fork can be opened for write access only when both forks of a file are empty. 
 449         ** So, with those rules to live with, we'll do those operations now while both forks 
 454                 /* We only set the file attributes now if the file is being copied into a 
 455                 ** drop box. In all other cases, it is better to set the attributes last 
 456                 ** so that if FileCopy is modified to give up time to other processes 
 457                 ** periodicly, the Finder won't try to read any bundle information (because 
 458                 ** the bundle-bit will still be clear) from a partially copied file. If the 
 459                 ** copy is into a drop box, we have to set the attributes now, but since the 
 460                 ** destination forks are opened with write/deny-read/deny-write permissions, 
 461                 ** any Finder that might see the file in the drop box won't be able to open 
 462                 ** its resource fork until the resource fork is closed. 
 464                 ** Note: if you do modify FileCopy to give up time to other processes, don't 
 465                 ** give up time between the time the destination file is created (above) and 
 466                 ** the time both forks are opened (below). That way, you stand the best chance 
 467                 ** of making sure the Finder doesn't read a partially copied resource fork. 
 469                 /* Copy attributes but don't lock the destination. */ 
 470                 err 
= CopyFileMgrAttributes(srcVRefNum
, srcDirID
, srcName
, 
 471                                                                         dstVRefNum
, dstDirID
, dstName
, false); 
 478         /* Attempt to copy the comments while both forks are empty. 
 479         ** Ignore the result because we really don't care if it worked or not. */ 
 480         (void) DTCopyComment(srcVRefNum
, srcDirID
, srcName
, dstVRefNum
, dstDirID
, dstName
); 
 482         /* See which forks we need to copy. By doing this, we won't create a data or resource fork 
 483         ** for the destination unless it's really needed (some foreign file systems such as 
 484         ** the ProDOS File System and Macintosh PC Exchange have to create additional disk 
 485         ** structures to support resource forks). */ 
 486         err 
= CheckForForks(srcVRefNum
, srcDirID
, srcName
, &hasDataFork
, &hasResourceFork
); 
 494                 /* Open the destination data fork. */ 
 495                 err 
= HOpenAware(dstVRefNum
, dstDirID
, dstName
, dstCopyMode
, &dstDataRefNum
); 
 502         if ( hasResourceFork 
) 
 504                 /* Open the destination resource fork. */ 
 505                 err 
= HOpenRFAware(dstVRefNum
, dstDirID
, dstName
, dstCopyMode
, &dstRsrcRefNum
); 
 514                 /* Copy the data fork. */ 
 515                 err 
= CopyFork(srcRefNum
, dstDataRefNum
, copyBufferPtr
, copyBufferSize
); 
 521                 /* Close both data forks and clear reference numbers. */ 
 522                 (void) FSClose(srcRefNum
); 
 523                 (void) FSClose(dstDataRefNum
); 
 524                 srcRefNum 
= dstDataRefNum 
= 0; 
 528                 /* Close the source data fork since it was opened earlier */ 
 529                 (void) FSClose(srcRefNum
); 
 533         if ( hasResourceFork 
) 
 535                 /* Open the source resource fork. */ 
 536                 err 
= HOpenRFAware(srcVRefNum
, srcDirID
, srcName
, srcCopyMode
, &srcRefNum
); 
 542                 /* Copy the resource fork. */ 
 543                 err 
= CopyFork(srcRefNum
, dstRsrcRefNum
, copyBufferPtr
, copyBufferSize
); 
 549                 /* Close both resource forks and clear reference numbers. */ 
 550                 (void) FSClose(srcRefNum
); 
 551                 (void) FSClose(dstRsrcRefNum
); 
 552                 srcRefNum 
= dstRsrcRefNum 
= 0; 
 555         /* Get rid of the copy buffer if we allocated it. */ 
 558                 DisposePtr((Ptr
)copyBufferPtr
); 
 561         /* Attempt to copy attributes again to set mod date.  Copy lock condition this time 
 562         ** since we're done with the copy operation.  This operation will fail if we're copying 
 563         ** into an AppleShare dropbox, so we don't check for error conditions. */ 
 564         CopyFileMgrAttributes(srcVRefNum
, srcDirID
, srcName
, 
 565                                                         dstVRefNum
, dstDirID
, dstName
, true); 
 567         /* Hey, we did it! */ 
 571         if ( srcRefNum 
!= 0 ) 
 573                 (void) FSClose(srcRefNum
);              /* Close the source file */ 
 575         if ( dstDataRefNum 
!= 0 ) 
 577                 (void) FSClose(dstDataRefNum
);  /* Close the destination file data fork */ 
 579         if ( dstRsrcRefNum 
!= 0 ) 
 581                 (void) FSClose(dstRsrcRefNum
);  /* Close the destination file resource fork */ 
 585                 (void) HDelete(dstVRefNum
, dstDirID
, dstName
);  /* Delete dest file.  This may fail if the file  
 586                                                                                                    is in a "drop folder" */ 
 588         if ( ourCopyBuffer 
)    /* dispose of any memory we allocated */ 
 590                 DisposePtr((Ptr
)copyBufferPtr
); 
 596 /*****************************************************************************/ 
 598 pascal  OSErr   
FSpFileCopy(const FSSpec 
*srcSpec
, 
 599                                                         const FSSpec 
*dstSpec
, 
 600                                                         ConstStr255Param copyName
, 
 605         return ( FileCopy(srcSpec
->vRefNum
, srcSpec
->parID
, srcSpec
->name
, 
 606                                          dstSpec
->vRefNum
, dstSpec
->parID
, dstSpec
->name
, 
 607                                          copyName
, copyBufferPtr
, copyBufferSize
, preflight
) ); 
 610 /*****************************************************************************/