]> git.saurik.com Git - wxWidgets.git/blob - src/mac/morefile/FileCopy.cpp
include wxWindows headers using "..." instead of <...>
[wxWidgets.git] / src / mac / morefile / FileCopy.cpp
1 /*
2 ** Apple Macintosh Developer Technical Support
3 **
4 ** FileCopy: A robust, general purpose file copy routine.
5 **
6 ** by Jim Luther, Apple Developer Technical Support Emeritus
7 **
8 ** File: FileCopy.c
9 **
10 ** Copyright © 1992-1998 Apple Computer, Inc.
11 ** All rights reserved.
12 **
13 ** You may incorporate this sample code into your applications without
14 ** restriction, though the sample code has been provided "AS IS" and the
15 ** responsibility for its operation is 100% yours. However, what you are
16 ** not permitted to do is to redistribute the source as "DSC Sample Code"
17 ** after having made changes. If you're going to re-distribute the source,
18 ** we require that you make it clear in the source that the code was
19 ** descended from Apple Sample Code, but that you've made changes.
20 */
21
22 #include <Types.h>
23 #include <Errors.h>
24 #include <Memory.h>
25 #include <Files.h>
26 #include <Math64.h>
27
28 #define __COMPILINGMOREFILES
29
30 #include "morefile.h"
31 #include "moreextr.h"
32 #include "moredesk.h"
33 #include "filecopy.h"
34
35 /*****************************************************************************/
36
37 /* local constants */
38
39 /* The deny-mode privileges to use when opening the source and destination files. */
40
41 enum
42 {
43 srcCopyMode = dmRdDenyWr,
44 dstCopyMode = dmWrDenyRdWr
45 };
46
47 /* The largest (16K) and smallest (.5K) copy buffer to use if the caller doesn't supply
48 ** their own copy buffer. */
49
50 enum
51 {
52 bigCopyBuffSize = 0x00004000,
53 minCopyBuffSize = 0x00000200
54 };
55
56 /*****************************************************************************/
57
58 /* static prototypes */
59
60 static OSErr GetDestinationDirInfo(short vRefNum,
61 long dirID,
62 ConstStr255Param name,
63 long *theDirID,
64 Boolean *isDirectory,
65 Boolean *isDropBox);
66 /* GetDestinationDirInfo tells us if the destination is a directory, it's
67 directory ID, and if it's an AppleShare drop box (write privileges only --
68 no read or search privileges).
69 vRefNum input: Volume specification.
70 dirID input: Directory ID.
71 name input: Pointer to object name, or nil when dirID
72 specifies a directory that's the object.
73 theDirID output: If the object is a file, then its parent directory
74 ID. If the object is a directory, then its ID.
75 isDirectory output: True if object is a directory; false if
76 object is a file.
77 isDropBox output: True if directory is an AppleShare drop box.
78 */
79
80 static OSErr CheckForForks(short vRefNum,
81 long dirID,
82 ConstStr255Param name,
83 Boolean *hasDataFork,
84 Boolean *hasResourceFork);
85 /* CheckForForks tells us if there is a data or resource fork to copy.
86 vRefNum input: Volume specification of the file's current
87 location.
88 dirID input: Directory ID of the file's current location.
89 name input: The name of the file.
90 */
91
92 static OSErr PreflightFileCopySpace(short srcVRefNum,
93 long srcDirID,
94 ConstStr255Param srcName,
95 ConstStr255Param dstVolName,
96 short dstVRefNum,
97 Boolean *spaceOK);
98 /* PreflightFileCopySpace determines if there's enough space on a
99 volume to copy the specified file to that volume.
100 Note: The results of this routine are not perfect. For example if the
101 volume's catalog or extents overflow file grows when the new file is
102 created, more allocation blocks may be needed beyond those needed for
103 the file's data and resource forks.
104
105 srcVRefNum input: Volume specification of the file's current
106 location.
107 srcDirID input: Directory ID of the file's current location.
108 srcName input: The name of the file.
109 dstVolName input: A pointer to the name of the volume where
110 the file will be copied or NULL.
111 dstVRefNum input: Volume specification indicating the volume
112 where the file will be copied.
113 spaceOK output: true if there's enough space on the volume for
114 the file's data and resource forks.
115 */
116
117 /*****************************************************************************/
118
119 static OSErr GetDestinationDirInfo(short vRefNum,
120 long dirID,
121 ConstStr255Param name,
122 long *theDirID,
123 Boolean *isDirectory,
124 Boolean *isDropBox)
125 {
126 CInfoPBRec pb;
127 OSErr error;
128
129 pb.dirInfo.ioACUser = 0; /* ioACUser used to be filler2, clear it before calling GetCatInfo */
130 error = GetCatInfoNoName(vRefNum, dirID, name, &pb);
131 *theDirID = pb.dirInfo.ioDrDirID;
132 *isDirectory = (pb.dirInfo.ioFlAttrib & ioDirMask) != 0;
133 /* see if access priviledges are make changes, not see folder, and not see files (drop box) */
134 *isDropBox = ((pb.dirInfo.ioACUser & 0x07) == 0x03);
135
136 return ( error );
137 }
138
139 /*****************************************************************************/
140
141 static OSErr CheckForForks(short vRefNum,
142 long dirID,
143 ConstStr255Param name,
144 Boolean *hasDataFork,
145 Boolean *hasResourceFork)
146 {
147 HParamBlockRec pb;
148 OSErr error;
149
150 pb.fileParam.ioNamePtr = (StringPtr)name;
151 pb.fileParam.ioVRefNum = vRefNum;
152 pb.fileParam.ioFVersNum = 0;
153 pb.fileParam.ioDirID = dirID;
154 pb.fileParam.ioFDirIndex = 0;
155 error = PBHGetFInfoSync(&pb);
156 *hasDataFork = (pb.fileParam.ioFlLgLen != 0);
157 *hasResourceFork = (pb.fileParam.ioFlRLgLen != 0);
158
159 return ( error );
160 }
161
162 /*****************************************************************************/
163
164 #if !TARGET_CARBON
165
166 static OSErr PreflightFileCopySpace(short srcVRefNum,
167 long srcDirID,
168 ConstStr255Param srcName,
169 ConstStr255Param dstVolName,
170 short dstVRefNum,
171 Boolean *spaceOK)
172 {
173 UniversalFMPB pb;
174 OSErr error;
175 unsigned long dstFreeBlocks;
176 unsigned long dstBlksPerAllocBlk;
177 unsigned long srcDataBlks;
178 unsigned long srcResourceBlks;
179
180 error = XGetVolumeInfoNoName(dstVolName, dstVRefNum, &pb.xPB);
181 if ( error == noErr )
182 {
183 /* get allocation block size (always multiple of 512) and divide by 512
184 to get number of 512-byte blocks per allocation block */
185 dstBlksPerAllocBlk = ((unsigned long)pb.xPB.ioVAlBlkSiz >> 9);
186
187 /* Convert freeBytes to free disk blocks (512-byte blocks) */
188 // dstFreeBlocks = (pb.xPB.ioVFreeBytes.hi << 23) + (pb.xPB.ioVFreeBytes.lo >> 9);
189 dstFreeBlocks = pb.xPB.ioVFreeBytes >> 9 ;
190
191 /* Now, get the size of the file's data resource forks */
192 pb.hPB.fileParam.ioNamePtr = (StringPtr)srcName;
193 pb.hPB.fileParam.ioVRefNum = srcVRefNum;
194 pb.hPB.fileParam.ioFVersNum = 0;
195 pb.hPB.fileParam.ioDirID = srcDirID;
196 pb.hPB.fileParam.ioFDirIndex = 0;
197 error = PBHGetFInfoSync(&pb.hPB);
198 if ( error == noErr )
199 {
200 /* Since space on Mac OS disks is always allocated in allocation blocks, */
201 /* this code takes into account rounding up to the end of an allocation block. */
202
203 /* get number of 512-byte blocks needed for data fork */
204 if ( ((unsigned long)pb.hPB.fileParam.ioFlLgLen & 0x000001ff) != 0 )
205 {
206 srcDataBlks = ((unsigned long)pb.hPB.fileParam.ioFlLgLen >> 9) + 1;
207 }
208 else
209 {
210 srcDataBlks = (unsigned long)pb.hPB.fileParam.ioFlLgLen >> 9;
211 }
212
213 /* now, calculate number of new allocation blocks needed */
214 if ( srcDataBlks % dstBlksPerAllocBlk )
215 {
216 srcDataBlks = (srcDataBlks / dstBlksPerAllocBlk) + 1;
217 }
218 else
219 {
220 srcDataBlks /= dstBlksPerAllocBlk;
221 }
222
223 /* get number of 512-byte blocks needed for resource fork */
224 if ( ((unsigned long)pb.hPB.fileParam.ioFlRLgLen & 0x000001ff) != 0 )
225 {
226 srcResourceBlks = ((unsigned long)pb.hPB.fileParam.ioFlRLgLen >> 9) + 1;
227 }
228 else
229 {
230 srcResourceBlks = (unsigned long)pb.hPB.fileParam.ioFlRLgLen >> 9;
231 }
232
233 /* now, calculate number of new allocation blocks needed */
234 if ( srcResourceBlks % dstBlksPerAllocBlk )
235 {
236 srcResourceBlks = (srcResourceBlks / dstBlksPerAllocBlk) + 1;
237 }
238 else
239 {
240 srcResourceBlks /= dstBlksPerAllocBlk;
241 }
242
243 /* Is there enough room on the destination volume for the source file? */
244 *spaceOK = ( ((srcDataBlks + srcResourceBlks) * dstBlksPerAllocBlk) <= dstFreeBlocks );
245 }
246 }
247
248 return ( error );
249 }
250 #endif
251 /*****************************************************************************/
252
253 pascal OSErr FileCopy(short srcVRefNum,
254 long srcDirID,
255 ConstStr255Param srcName,
256 short dstVRefNum,
257 long dstDirID,
258 ConstStr255Param dstPathname,
259 ConstStr255Param copyName,
260 void *copyBufferPtr,
261 long copyBufferSize,
262 Boolean preflight)
263 {
264 OSErr err;
265
266 short srcRefNum = 0, /* 0 when source data and resource fork are closed */
267 dstDataRefNum = 0, /* 0 when destination data fork is closed */
268 dstRsrcRefNum = 0; /* 0 when destination resource fork is closed */
269
270 Str63 dstName; /* The filename of the destination. It might be the
271 ** source filename, it might be a new name... */
272
273 GetVolParmsInfoBuffer infoBuffer; /* Where PBGetVolParms dumps its info */
274 long srcServerAdr; /* AppleTalk server address of source (if any) */
275
276 Boolean dstCreated = false, /* true when destination file has been created */
277 ourCopyBuffer = false, /* true if we had to allocate the copy buffer */
278 isDirectory, /* true if destination is really a directory */
279 isDropBox; /* true if destination is an AppleShare drop box */
280
281 long tempLong;
282 short tempInt;
283
284 Boolean spaceOK; /* true if there's enough room to copy the file to the destination volume */
285
286 Boolean hasDataFork;
287 Boolean hasResourceFork;
288
289 /* Preflight for size */
290 if ( preflight )
291 {
292 err = PreflightFileCopySpace(srcVRefNum, srcDirID, srcName,
293 dstPathname, dstVRefNum, &spaceOK);
294 if ( err != noErr )
295 {
296 return ( err );
297 }
298
299 if ( !spaceOK )
300 {
301 return ( dskFulErr );
302 }
303 }
304
305 /* get the destination's real dirID and make sure it really is a directory */
306 err = GetDestinationDirInfo(dstVRefNum, dstDirID, dstPathname,
307 &dstDirID, &isDirectory, &isDropBox);
308 if ( err != noErr )
309 {
310 goto ErrorExit;
311 }
312
313 if ( !isDirectory )
314 {
315 return ( dirNFErr );
316 }
317
318 /* get the destination's real vRefNum */
319 err = DetermineVRefNum(dstPathname, dstVRefNum, &dstVRefNum);
320 if ( err != noErr )
321 {
322 goto ErrorExit;
323 }
324
325 /* See if PBHCopyFile can be used. Using PBHCopyFile saves time by letting the file server
326 ** copy the file if the source and destination locations are on the same file server. */
327 tempLong = sizeof(infoBuffer);
328 err = HGetVolParms(srcName, srcVRefNum, &infoBuffer, &tempLong);
329 if ( (err != noErr) && (err != paramErr) )
330 {
331 return ( err );
332 }
333
334 if ( (err != paramErr) && hasCopyFile(infoBuffer) )
335 {
336 /* The source volume supports PBHCopyFile. */
337 srcServerAdr = infoBuffer.vMServerAdr;
338
339 /* Now, see if the destination volume is on the same file server. */
340 tempLong = sizeof(infoBuffer);
341 err = HGetVolParms(NULL, dstVRefNum, &infoBuffer, &tempLong);
342 if ( (err != noErr) && (err != paramErr) )
343 {
344 return ( err );
345 }
346 if ( (err != paramErr) && (srcServerAdr == infoBuffer.vMServerAdr) )
347 {
348 /* Source and Dest are on same server and PBHCopyFile is supported. Copy with CopyFile. */
349 err = HCopyFile(srcVRefNum, srcDirID, srcName, dstVRefNum, dstDirID, NULL, copyName);
350 if ( err != noErr )
351 {
352 return ( err );
353 }
354
355 /* AppleShare's CopyFile clears the isAlias bit, so I still need to attempt to copy
356 the File's attributes to attempt to get things right. */
357 if ( copyName != NULL ) /* Did caller supply copy file name? */
358 {
359 /* Yes, use the caller supplied copy file name. */
360 (void) CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName,
361 dstVRefNum, dstDirID, copyName, true);
362 }
363 else
364 {
365 /* They didn't, so get the source file name and use it. */
366 if ( GetFilenameFromPathname(srcName, dstName) == noErr )
367 {
368 /* */
369 (void) CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName,
370 dstVRefNum, dstDirID, dstName, true);
371 }
372 }
373 return ( err );
374 }
375 }
376
377 /* If we're here, then PBHCopyFile couldn't be used so we have to copy the file by hand. */
378
379 /* Make sure a copy buffer is allocated. */
380 if ( copyBufferPtr == NULL )
381 {
382 /* The caller didn't supply a copy buffer so grab one from the application heap.
383 ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer.
384 ** If 512 bytes aren't available, we're in trouble. */
385 copyBufferSize = bigCopyBuffSize;
386 copyBufferPtr = NewPtr(copyBufferSize);
387 if ( copyBufferPtr == NULL )
388 {
389 copyBufferSize = minCopyBuffSize;
390 copyBufferPtr = NewPtr(copyBufferSize);
391 if ( copyBufferPtr == NULL )
392 {
393 return ( memFullErr );
394 }
395 }
396 ourCopyBuffer = true;
397 }
398
399 /* Open the source data fork. */
400 err = HOpenAware(srcVRefNum, srcDirID, srcName, srcCopyMode, &srcRefNum);
401 if ( err != noErr )
402 return ( err );
403
404 /* Once a file is opened, we have to exit via ErrorExit to make sure things are cleaned up */
405
406 /* See if the copy will be renamed. */
407 if ( copyName != NULL ) /* Did caller supply copy file name? */
408 BlockMoveData(copyName, dstName, copyName[0] + 1); /* Yes, use the caller supplied copy file name. */
409 else
410 { /* They didn't, so get the source file name and use it. */
411 err = GetFileLocation(srcRefNum, &tempInt, &tempLong, dstName);
412 if ( err != noErr )
413 {
414 goto ErrorExit;
415 }
416 }
417
418 /* Create the destination file. */
419 err = HCreateMinimum(dstVRefNum, dstDirID, dstName);
420 if ( err != noErr )
421 {
422 goto ErrorExit;
423 }
424 dstCreated = true; /* After creating the destination file, any
425 ** error conditions should delete the destination file */
426
427 /* An AppleShare dropbox folder is a folder for which the user has the Make Changes
428 ** privilege (write access), but not See Files (read access) and See Folders (search access).
429 ** Copying a file into an AppleShare dropbox presents some special problems. Here are the
430 ** rules we have to follow to copy a file into a dropbox:
431 ** ¥ File attributes can be changed only when both forks of a file are empty.
432 ** ¥ DeskTop Manager comments can be added to a file only when both forks of a file
433 ** are empty.
434 ** ¥ A fork can be opened for write access only when both forks of a file are empty.
435 ** So, with those rules to live with, we'll do those operations now while both forks
436 ** are empty. */
437
438 if ( isDropBox )
439 {
440 /* We only set the file attributes now if the file is being copied into a
441 ** drop box. In all other cases, it is better to set the attributes last
442 ** so that if FileCopy is modified to give up time to other processes
443 ** periodicly, the Finder won't try to read any bundle information (because
444 ** the bundle-bit will still be clear) from a partially copied file. If the
445 ** copy is into a drop box, we have to set the attributes now, but since the
446 ** destination forks are opened with write/deny-read/deny-write permissions,
447 ** any Finder that might see the file in the drop box won't be able to open
448 ** its resource fork until the resource fork is closed.
449 **
450 ** Note: if you do modify FileCopy to give up time to other processes, don't
451 ** give up time between the time the destination file is created (above) and
452 ** the time both forks are opened (below). That way, you stand the best chance
453 ** of making sure the Finder doesn't read a partially copied resource fork.
454 */
455 /* Copy attributes but don't lock the destination. */
456 err = CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName,
457 dstVRefNum, dstDirID, dstName, false);
458 if ( err != noErr )
459 {
460 goto ErrorExit;
461 }
462 }
463
464 /* Attempt to copy the comments while both forks are empty.
465 ** Ignore the result because we really don't care if it worked or not. */
466 (void) DTCopyComment(srcVRefNum, srcDirID, srcName, dstVRefNum, dstDirID, dstName);
467
468 /* See which forks we need to copy. By doing this, we won't create a data or resource fork
469 ** for the destination unless it's really needed (some foreign file systems such as
470 ** the ProDOS File System and Macintosh PC Exchange have to create additional disk
471 ** structures to support resource forks). */
472 err = CheckForForks(srcVRefNum, srcDirID, srcName, &hasDataFork, &hasResourceFork);
473 if ( err != noErr )
474 {
475 goto ErrorExit;
476 }
477
478 if ( hasDataFork )
479 {
480 /* Open the destination data fork. */
481 err = HOpenAware(dstVRefNum, dstDirID, dstName, dstCopyMode, &dstDataRefNum);
482 if ( err != noErr )
483 {
484 goto ErrorExit;
485 }
486 }
487
488 if ( hasResourceFork )
489 {
490 /* Open the destination resource fork. */
491 err = HOpenRFAware(dstVRefNum, dstDirID, dstName, dstCopyMode, &dstRsrcRefNum);
492 if ( err != noErr )
493 {
494 goto ErrorExit;
495 }
496 }
497
498 if ( hasDataFork )
499 {
500 /* Copy the data fork. */
501 err = CopyFork(srcRefNum, dstDataRefNum, copyBufferPtr, copyBufferSize);
502 if ( err != noErr )
503 {
504 goto ErrorExit;
505 }
506
507 /* Close both data forks and clear reference numbers. */
508 (void) FSClose(srcRefNum);
509 (void) FSClose(dstDataRefNum);
510 srcRefNum = dstDataRefNum = 0;
511 }
512 else
513 {
514 /* Close the source data fork since it was opened earlier */
515 (void) FSClose(srcRefNum);
516 srcRefNum = 0;
517 }
518
519 if ( hasResourceFork )
520 {
521 /* Open the source resource fork. */
522 err = HOpenRFAware(srcVRefNum, srcDirID, srcName, srcCopyMode, &srcRefNum);
523 if ( err != noErr )
524 {
525 goto ErrorExit;
526 }
527
528 /* Copy the resource fork. */
529 err = CopyFork(srcRefNum, dstRsrcRefNum, copyBufferPtr, copyBufferSize);
530 if ( err != noErr )
531 {
532 goto ErrorExit;
533 }
534
535 /* Close both resource forks and clear reference numbers. */
536 (void) FSClose(srcRefNum);
537 (void) FSClose(dstRsrcRefNum);
538 srcRefNum = dstRsrcRefNum = 0;
539 }
540
541 /* Get rid of the copy buffer if we allocated it. */
542 if ( ourCopyBuffer )
543 {
544 DisposePtr((Ptr)copyBufferPtr);
545 }
546
547 /* Attempt to copy attributes again to set mod date. Copy lock condition this time
548 ** since we're done with the copy operation. This operation will fail if we're copying
549 ** into an AppleShare dropbox, so we don't check for error conditions. */
550 CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName,
551 dstVRefNum, dstDirID, dstName, true);
552
553 /* Hey, we did it! */
554 return ( noErr );
555
556 ErrorExit:
557 if ( srcRefNum != 0 )
558 {
559 (void) FSClose(srcRefNum); /* Close the source file */
560 }
561 if ( dstDataRefNum != 0 )
562 {
563 (void) FSClose(dstDataRefNum); /* Close the destination file data fork */
564 }
565 if ( dstRsrcRefNum != 0 )
566 {
567 (void) FSClose(dstRsrcRefNum); /* Close the destination file resource fork */
568 }
569 if ( dstCreated )
570 {
571 (void) HDelete(dstVRefNum, dstDirID, dstName); /* Delete dest file. This may fail if the file
572 is in a "drop folder" */
573 }
574 if ( ourCopyBuffer ) /* dispose of any memory we allocated */
575 {
576 DisposePtr((Ptr)copyBufferPtr);
577 }
578
579 return ( err );
580 }
581
582 /*****************************************************************************/
583
584 pascal OSErr FSpFileCopy(const FSSpec *srcSpec,
585 const FSSpec *dstSpec,
586 ConstStr255Param copyName,
587 void *copyBufferPtr,
588 long copyBufferSize,
589 Boolean preflight)
590 {
591 return ( FileCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
592 dstSpec->vRefNum, dstSpec->parID, dstSpec->name,
593 copyName, copyBufferPtr, copyBufferSize, preflight) );
594 }
595
596 /*****************************************************************************/
597