]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/morefile/FileCopy.c
cleanup - added some error checking, reformatting
[wxWidgets.git] / src / mac / carbon / morefile / FileCopy.c
1 /*
2 File: FileCopy.c
3
4 Contains: A robust, general purpose file copy routine.
5
6 Version: MoreFiles
7
8 Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved.
9
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.
17
18 File Ownership:
19
20 DRI: Apple Macintosh Developer Technical Support
21
22 Other Contact: Apple Macintosh Developer Technical Support
23 <http://developer.apple.com/bugreporter/>
24
25 Technology: DTS Sample Code
26
27 Writers:
28
29 (JL) Jim Luther
30
31 Change History (most recent first):
32
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.
37 */
38
39 #include <MacTypes.h>
40 #include <MacErrors.h>
41 #include <MacMemory.h>
42 #include <Files.h>
43 #include <Math64.h>
44
45 #define __COMPILINGMOREFILES
46
47 #include "MoreFiles.h"
48 #include "MoreFilesExtras.h"
49 #include "MoreDesktopMgr.h"
50 #include "FileCopy.h"
51
52 /*****************************************************************************/
53
54 /* local constants */
55
56 /* The deny-mode privileges to use when opening the source and destination files. */
57
58 enum
59 {
60 srcCopyMode = dmRdDenyWr,
61 dstCopyMode = dmWrDenyRdWr
62 };
63
64 /* The largest (16K) and smallest (.5K) copy buffer to use if the caller doesn't supply
65 ** their own copy buffer. */
66
67 enum
68 {
69 bigCopyBuffSize = 0x00004000,
70 minCopyBuffSize = 0x00000200
71 };
72
73 /*****************************************************************************/
74
75 /* static prototypes */
76
77 static OSErr GetDestinationDirInfo(short vRefNum,
78 long dirID,
79 ConstStr255Param name,
80 long *theDirID,
81 Boolean *isDirectory,
82 Boolean *isDropBox);
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
93 object is a file.
94 isDropBox output: True if directory is an AppleShare drop box.
95 */
96
97 static OSErr CheckForForks(short vRefNum,
98 long dirID,
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
104 location.
105 dirID input: Directory ID of the file's current location.
106 name input: The name of the file.
107 */
108
109 static OSErr PreflightFileCopySpace(short srcVRefNum,
110 long srcDirID,
111 ConstStr255Param srcName,
112 ConstStr255Param dstVolName,
113 short dstVRefNum,
114 Boolean *spaceOK);
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.
121
122 srcVRefNum input: Volume specification of the file's current
123 location.
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.
132 */
133
134 /*****************************************************************************/
135
136 static OSErr GetDestinationDirInfo(short vRefNum,
137 long dirID,
138 ConstStr255Param name,
139 long *theDirID,
140 Boolean *isDirectory,
141 Boolean *isDropBox)
142 {
143 CInfoPBRec pb;
144 OSErr error;
145
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);
152
153 return ( error );
154 }
155
156 /*****************************************************************************/
157
158 static OSErr CheckForForks(short vRefNum,
159 long dirID,
160 ConstStr255Param name,
161 Boolean *hasDataFork,
162 Boolean *hasResourceFork)
163 {
164 HParamBlockRec pb;
165 OSErr error;
166
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);
175
176 return ( error );
177 }
178
179 /*****************************************************************************/
180
181 static OSErr PreflightFileCopySpace(short srcVRefNum,
182 long srcDirID,
183 ConstStr255Param srcName,
184 ConstStr255Param dstVolName,
185 short dstVRefNum,
186 Boolean *spaceOK)
187 {
188 UniversalFMPB pb;
189 OSErr error;
190 unsigned long dstFreeBlocks;
191 unsigned long dstBlksPerAllocBlk;
192 unsigned long srcDataBlks;
193 unsigned long srcResourceBlks;
194
195 error = XGetVolumeInfoNoName(dstVolName, dstVRefNum, &pb.xPB);
196 if ( error == noErr )
197 {
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);
201
202 /* Convert freeBytes to free disk blocks (512-byte blocks) */
203 dstFreeBlocks = U32SetU(U64ShiftRight(pb.xPB.ioVFreeBytes, 9));
204
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 )
213 {
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. */
216
217 /* get number of 512-byte blocks needed for data fork */
218 if ( ((unsigned long)pb.hPB.fileParam.ioFlLgLen & 0x000001ff) != 0 )
219 {
220 srcDataBlks = ((unsigned long)pb.hPB.fileParam.ioFlLgLen >> 9) + 1;
221 }
222 else
223 {
224 srcDataBlks = (unsigned long)pb.hPB.fileParam.ioFlLgLen >> 9;
225 }
226
227 /* now, calculate number of new allocation blocks needed */
228 if ( srcDataBlks % dstBlksPerAllocBlk )
229 {
230 srcDataBlks = (srcDataBlks / dstBlksPerAllocBlk) + 1;
231 }
232 else
233 {
234 srcDataBlks /= dstBlksPerAllocBlk;
235 }
236
237 /* get number of 512-byte blocks needed for resource fork */
238 if ( ((unsigned long)pb.hPB.fileParam.ioFlRLgLen & 0x000001ff) != 0 )
239 {
240 srcResourceBlks = ((unsigned long)pb.hPB.fileParam.ioFlRLgLen >> 9) + 1;
241 }
242 else
243 {
244 srcResourceBlks = (unsigned long)pb.hPB.fileParam.ioFlRLgLen >> 9;
245 }
246
247 /* now, calculate number of new allocation blocks needed */
248 if ( srcResourceBlks % dstBlksPerAllocBlk )
249 {
250 srcResourceBlks = (srcResourceBlks / dstBlksPerAllocBlk) + 1;
251 }
252 else
253 {
254 srcResourceBlks /= dstBlksPerAllocBlk;
255 }
256
257 /* Is there enough room on the destination volume for the source file? */
258 *spaceOK = ( ((srcDataBlks + srcResourceBlks) * dstBlksPerAllocBlk) <= dstFreeBlocks );
259 }
260 }
261
262 return ( error );
263 }
264
265 /*****************************************************************************/
266
267 pascal OSErr FileCopy(short srcVRefNum,
268 long srcDirID,
269 ConstStr255Param srcName,
270 short dstVRefNum,
271 long dstDirID,
272 ConstStr255Param dstPathname,
273 ConstStr255Param copyName,
274 void *copyBufferPtr,
275 long copyBufferSize,
276 Boolean preflight)
277 {
278 OSErr err;
279
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 */
283
284 Str63 dstName; /* The filename of the destination. It might be the
285 ** source filename, it might be a new name... */
286
287 GetVolParmsInfoBuffer infoBuffer; /* Where PBGetVolParms dumps its info */
288 long srcServerAdr; /* AppleTalk server address of source (if any) */
289
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 */
294
295 long tempLong;
296 short tempInt;
297
298 Boolean spaceOK; /* true if there's enough room to copy the file to the destination volume */
299
300 Boolean hasDataFork;
301 Boolean hasResourceFork;
302
303 /* Preflight for size */
304 if ( preflight )
305 {
306 err = PreflightFileCopySpace(srcVRefNum, srcDirID, srcName,
307 dstPathname, dstVRefNum, &spaceOK);
308 if ( err != noErr )
309 {
310 return ( err );
311 }
312
313 if ( !spaceOK )
314 {
315 return ( dskFulErr );
316 }
317 }
318
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);
322 if ( err != noErr )
323 {
324 goto ErrorExit;
325 }
326
327 if ( !isDirectory )
328 {
329 return ( dirNFErr );
330 }
331
332 /* get the destination's real vRefNum */
333 err = DetermineVRefNum(dstPathname, dstVRefNum, &dstVRefNum);
334 if ( err != noErr )
335 {
336 goto ErrorExit;
337 }
338
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) )
344 {
345 return ( err );
346 }
347
348 if ( (err != paramErr) && hasCopyFile(&infoBuffer) )
349 {
350 /* The source volume supports PBHCopyFile. */
351 srcServerAdr = infoBuffer.vMServerAdr;
352
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) )
357 {
358 return ( err );
359 }
360 if ( (err != paramErr) && (srcServerAdr == infoBuffer.vMServerAdr) )
361 {
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);
364 if ( err != noErr )
365 {
366 return ( err );
367 }
368
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? */
372 {
373 /* Yes, use the caller supplied copy file name. */
374 (void) CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName,
375 dstVRefNum, dstDirID, copyName, true);
376 }
377 else
378 {
379 /* They didn't, so get the source file name and use it. */
380 if ( GetFilenameFromPathname(srcName, dstName) == noErr )
381 {
382 /* */
383 (void) CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName,
384 dstVRefNum, dstDirID, dstName, true);
385 }
386 }
387 return ( err );
388 }
389 }
390
391 /* If we're here, then PBHCopyFile couldn't be used so we have to copy the file by hand. */
392
393 /* Make sure a copy buffer is allocated. */
394 if ( copyBufferPtr == NULL )
395 {
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 )
402 {
403 copyBufferSize = minCopyBuffSize;
404 copyBufferPtr = NewPtr(copyBufferSize);
405 if ( copyBufferPtr == NULL )
406 {
407 return ( memFullErr );
408 }
409 }
410 ourCopyBuffer = true;
411 }
412
413 /* Open the source data fork. */
414 err = HOpenAware(srcVRefNum, srcDirID, srcName, srcCopyMode, &srcRefNum);
415 if ( err != noErr )
416 return ( err );
417
418 /* Once a file is opened, we have to exit via ErrorExit to make sure things are cleaned up */
419
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. */
423 else
424 { /* They didn't, so get the source file name and use it. */
425 err = GetFileLocation(srcRefNum, &tempInt, &tempLong, dstName);
426 if ( err != noErr )
427 {
428 goto ErrorExit;
429 }
430 }
431
432 /* Create the destination file. */
433 err = HCreateMinimum(dstVRefNum, dstDirID, dstName);
434 if ( err != noErr )
435 {
436 goto ErrorExit;
437 }
438 dstCreated = true; /* After creating the destination file, any
439 ** error conditions should delete the destination file */
440
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
447 ** are empty.
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
450 ** are empty. */
451
452 if ( isDropBox )
453 {
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.
463 **
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.
468 */
469 /* Copy attributes but don't lock the destination. */
470 err = CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName,
471 dstVRefNum, dstDirID, dstName, false);
472 if ( err != noErr )
473 {
474 goto ErrorExit;
475 }
476 }
477
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);
481
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);
487 if ( err != noErr )
488 {
489 goto ErrorExit;
490 }
491
492 if ( hasDataFork )
493 {
494 /* Open the destination data fork. */
495 err = HOpenAware(dstVRefNum, dstDirID, dstName, dstCopyMode, &dstDataRefNum);
496 if ( err != noErr )
497 {
498 goto ErrorExit;
499 }
500 }
501
502 if ( hasResourceFork )
503 {
504 /* Open the destination resource fork. */
505 err = HOpenRFAware(dstVRefNum, dstDirID, dstName, dstCopyMode, &dstRsrcRefNum);
506 if ( err != noErr )
507 {
508 goto ErrorExit;
509 }
510 }
511
512 if ( hasDataFork )
513 {
514 /* Copy the data fork. */
515 err = CopyFork(srcRefNum, dstDataRefNum, copyBufferPtr, copyBufferSize);
516 if ( err != noErr )
517 {
518 goto ErrorExit;
519 }
520
521 /* Close both data forks and clear reference numbers. */
522 (void) FSClose(srcRefNum);
523 (void) FSClose(dstDataRefNum);
524 srcRefNum = dstDataRefNum = 0;
525 }
526 else
527 {
528 /* Close the source data fork since it was opened earlier */
529 (void) FSClose(srcRefNum);
530 srcRefNum = 0;
531 }
532
533 if ( hasResourceFork )
534 {
535 /* Open the source resource fork. */
536 err = HOpenRFAware(srcVRefNum, srcDirID, srcName, srcCopyMode, &srcRefNum);
537 if ( err != noErr )
538 {
539 goto ErrorExit;
540 }
541
542 /* Copy the resource fork. */
543 err = CopyFork(srcRefNum, dstRsrcRefNum, copyBufferPtr, copyBufferSize);
544 if ( err != noErr )
545 {
546 goto ErrorExit;
547 }
548
549 /* Close both resource forks and clear reference numbers. */
550 (void) FSClose(srcRefNum);
551 (void) FSClose(dstRsrcRefNum);
552 srcRefNum = dstRsrcRefNum = 0;
553 }
554
555 /* Get rid of the copy buffer if we allocated it. */
556 if ( ourCopyBuffer )
557 {
558 DisposePtr((Ptr)copyBufferPtr);
559 }
560
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);
566
567 /* Hey, we did it! */
568 return ( noErr );
569
570 ErrorExit:
571 if ( srcRefNum != 0 )
572 {
573 (void) FSClose(srcRefNum); /* Close the source file */
574 }
575 if ( dstDataRefNum != 0 )
576 {
577 (void) FSClose(dstDataRefNum); /* Close the destination file data fork */
578 }
579 if ( dstRsrcRefNum != 0 )
580 {
581 (void) FSClose(dstRsrcRefNum); /* Close the destination file resource fork */
582 }
583 if ( dstCreated )
584 {
585 (void) HDelete(dstVRefNum, dstDirID, dstName); /* Delete dest file. This may fail if the file
586 is in a "drop folder" */
587 }
588 if ( ourCopyBuffer ) /* dispose of any memory we allocated */
589 {
590 DisposePtr((Ptr)copyBufferPtr);
591 }
592
593 return ( err );
594 }
595
596 /*****************************************************************************/
597
598 pascal OSErr FSpFileCopy(const FSSpec *srcSpec,
599 const FSSpec *dstSpec,
600 ConstStr255Param copyName,
601 void *copyBufferPtr,
602 long copyBufferSize,
603 Boolean preflight)
604 {
605 return ( FileCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
606 dstSpec->vRefNum, dstSpec->parID, dstSpec->name,
607 copyName, copyBufferPtr, copyBufferSize, preflight) );
608 }
609
610 /*****************************************************************************/
611