aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/mac_updater/FSCopyObject.c
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/mac_updater/FSCopyObject.c
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/mac_updater/FSCopyObject.c')
-rw-r--r--linden/indra/mac_updater/FSCopyObject.c1986
1 files changed, 1986 insertions, 0 deletions
diff --git a/linden/indra/mac_updater/FSCopyObject.c b/linden/indra/mac_updater/FSCopyObject.c
new file mode 100644
index 0000000..b9210d2
--- /dev/null
+++ b/linden/indra/mac_updater/FSCopyObject.c
@@ -0,0 +1,1986 @@
1/*
2 File: FSCopyObject.c
3
4 Contains: A Copy/Delete Files/Folders engine which uses HFS+ API's.
5 This code takes some tricks/techniques from MoreFilesX and
6 MPFileCopy, wraps them all up into an easy to use API, and
7 adds a bunch of features. It will run on Mac OS 9.1 through
8 9.2.x and 10.1.x and up (Classic, Carbon and Mach-O)
9
10 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
11 ("Apple") in consideration of your agreement to the following terms, and your
12 use, installation, modification or redistribution of this Apple software
13 constitutes acceptance of these terms. If you do not agree with these terms,
14 please do not use, install, modify or redistribute this Apple software.
15
16 In consideration of your agreement to abide by the following terms, and subject
17 to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
18 copyrights in this original Apple software (the "Apple Software"), to use,
19 reproduce, modify and redistribute the Apple Software, with or without
20 modifications, in source and/or binary forms; provided that if you redistribute
21 the Apple Software in its entirety and without modifications, you must retain
22 this notice and the following text and disclaimers in all such redistributions of
23 the Apple Software. Neither the name, trademarks, service marks or logos of
24 Apple Computer, Inc. may be used to endorse or promote products derived from the
25 Apple Software without specific prior written permission from Apple. Except as
26 expressly stated in this notice, no other rights or licenses, express or implied,
27 are granted by Apple herein, including but not limited to any patent rights that
28 may be infringed by your derivative works or by other works in which the Apple
29 Software may be incorporated.
30
31 The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
32 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
33 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
34 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
35 COMBINATION WITH YOUR PRODUCTS.
36
37 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
38 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
39 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
41 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
42 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
43 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44
45 Copyright © 2002-2004 Apple Computer, Inc., All Rights Reserved
46*/
47
48#include "FSCopyObject.h"
49#include "GenLinkedList.h"
50#if !TARGET_API_MAC_OSX
51#include <UnicodeConverter.h>
52#endif
53#include <stddef.h>
54#include <string.h>
55
56#pragma mark ----- Tunable Parameters -----
57
58/* The following constants control the behavior of the copy engine. */
59
60enum { /* BufferSizeForVolSpeed */
61/* kDefaultCopyBufferSize = 2L * 1024 * 1024,*/ /* 2MB, Fast but not very responsive. */
62 kDefaultCopyBufferSize = 256L * 1024, /* 256kB, Slower but can still use machine. */
63 kMaximumCopyBufferSize = 2L * 1024 * 1024,
64 kMinimumCopyBufferSize = 1024
65};
66
67enum { /* CheckForDestInsideSrc */
68 errFSDestInsideSource = -1234
69};
70
71enum {
72 /* for use with PBHGetDirAccess in IsDropBox */
73 kPrivilegesMask = kioACAccessUserWriteMask | kioACAccessUserReadMask | kioACAccessUserSearchMask,
74
75 /* for use with FSGetCatalogInfo and FSPermissionInfo->mode */
76 /* from sys/stat.h... note -- sys/stat.h definitions are in octal */
77 /* */
78 /* You can use these values to adjust the users/groups permissions */
79 /* on a file/folder with FSSetCatalogInfo and extracting the */
80 /* kFSCatInfoPermissions field. See code below for examples */
81 kRWXUserAccessMask = 0x01C0,
82 kReadAccessUser = 0x0100,
83 kWriteAccessUser = 0x0080,
84 kExecuteAccessUser = 0x0040,
85
86 kRWXGroupAccessMask = 0x0038,
87 kReadAccessGroup = 0x0020,
88 kWriteAccessGroup = 0x0010,
89 kExecuteAccessGroup = 0x0008,
90
91 kRWXOtherAccessMask = 0x0007,
92 kReadAccessOther = 0x0004,
93 kWriteAccessOther = 0x0002,
94 kExecuteAccessOther = 0x0001,
95
96 kDropFolderValue = kWriteAccessOther | kExecuteAccessOther
97};
98
99#define kNumObjects 80
100
101#define VolHasCopyFile(volParms) (((volParms)->vMAttrib & (1L << bHasCopyFile)) != 0)
102
103#pragma mark ----- Struct Definitions -----
104
105 /* The CopyParams data structure holds the copy buffer used */
106 /* when copying the forks over, as well as special case */
107 /* info on the destination */
108struct CopyParams {
109 void *copyBuffer;
110 ByteCount copyBufferSize;
111 Boolean copyingToDropFolder;
112 Boolean copyingToLocalVolume;
113 Boolean volHasCopyFile;
114 DupeAction dupeAction;
115};
116typedef struct CopyParams CopyParams;
117
118 /* The FilterParams data structure holds the date and info */
119 /* that the caller wants passed into the Filter Proc, as well */
120 /* as the Filter Proc Pointer itself */
121struct FilterParams {
122 FSCatalogInfoBitmap whichInfo;
123 CopyObjectFilterProcPtr filterProcPtr;
124 Boolean containerChanged;
125 Boolean wantSpec;
126 Boolean wantName;
127 void *yourDataPtr;
128};
129typedef struct FilterParams FilterParams;
130
131 /* The ForkTracker data structure holds information about a specific fork, */
132 /* specifically the name and the refnum. We use this to build a list of */
133 /* all the forks before we start copying them. We need to do this because */
134 /* if we're copying into a drop folder, we must open all the forks before */
135 /* we start copying data into any of them. */
136 /* Plus it's a convenient way to keep track of all the forks... */
137struct ForkTracker {
138 HFSUniStr255 forkName;
139 SInt64 forkSize;
140 SInt16 forkDestRefNum;
141};
142typedef struct ForkTracker ForkTracker;
143typedef ForkTracker *ForkTrackerPtr;
144
145 /* The FolderListData data structure holds FSRefs to the source and */
146 /* coorisponding destination folder, as well as which level its on */
147 /* for use in ProcessFolderList. */
148struct FolderListData
149{
150 FSRef sourceDirRef;
151 FSRef destDirRef;
152 UInt32 level;
153};
154typedef struct FolderListData FolderListData;
155
156 /* The FSCopyFolderGlobals data structure holds the information needed to */
157 /* copy a directory */
158struct FSCopyFolderGlobals
159{
160 FSRef *sourceDirRef;
161 FSRef *destDirRef;
162
163 FSCatalogInfo *catInfoList;
164 FSRef *srcRefList;
165 HFSUniStr255 *nameList;
166
167 GenLinkedList folderList;
168 GenIteratorPtr folderListIter;
169
170 CopyParams *copyParams;
171 FilterParams *filterParams;
172 Boolean containerChanged;
173
174 ItemCount maxLevels;
175 ItemCount currentLevel;
176};
177typedef struct FSCopyFolderGlobals FSCopyFolderGlobals;
178
179 /* The FSDeleteObjectGlobals data structure holds information needed to */
180 /* recursively delete a directory */
181struct FSDeleteObjectGlobals
182{
183 FSCatalogInfo catalogInfo; /* FSCatalogInfo */
184 ItemCount actualObjects; /* number of objects returned */
185 OSErr result; /* result */
186};
187typedef struct FSDeleteObjectGlobals FSDeleteObjectGlobals;
188
189#pragma mark ----- Local Prototypes -----
190
191static OSErr FSCopyObjectPreflight ( const FSRef *source,
192 const FSRef *destDir,
193 const DupeAction dupeAction,
194 FSCatalogInfo *sourceCatInfo,
195 CopyParams *copyParams, /* can be NULL */
196 HFSUniStr255 *newObjectName,
197 FSRef *deleteMeRef,
198 Boolean *isReplacing,
199 Boolean *isDirectory );
200
201static OSErr FSCopyFile ( const FSRef *source,
202 const FSRef *destDir,
203 const FSCatalogInfo *sourceCatInfo,
204 const HFSUniStr255 *newFileName,
205 CopyParams *copyParams,
206 FilterParams *filterParams,
207 FSRef *newFileRef, /* can be NULL */
208 FSSpec *newFileSpec ); /* can be NULL */
209
210static OSErr CopyFile ( const FSRef *source,
211 FSCatalogInfo *sourceCatInfo,
212 const FSRef *destDir,
213 const HFSUniStr255 *destName, /* can be NULL */
214 CopyParams *copyParams,
215 FSRef *newRef, /* can be NULL */
216 FSSpec *newSpec ); /* can be NULL */
217
218static OSErr FSUsePBHCopyFile ( const FSRef *srcFileRef,
219 const FSRef *dstDirectoryRef,
220 const HFSUniStr255 *destName, /* can be NULL (no rename during copy) */
221 TextEncoding textEncodingHint,
222 FSRef *newRef, /* can be NULL */
223 FSSpec *newSpec ); /* can be NULL */
224
225static OSErr DoCopyFile ( const FSRef *source,
226 FSCatalogInfo *sourceCatInfo,
227 const FSRef *destDir,
228 const HFSUniStr255 *destName,
229 CopyParams *params,
230 FSRef *newRef, /* can be NULL */
231 FSSpec *newSpec ); /* can be NULL */
232
233static OSErr FSCopyFolder ( const FSRef *source,
234 const FSRef *destDir,
235 const FSCatalogInfo *sourceCatInfo,
236 const HFSUniStr255 *newFoldName,
237 CopyParams *copyParams,
238 FilterParams *filterParams,
239 ItemCount maxLevels,
240 FSRef *outDirRef, /* can be NULL */
241 FSSpec *outDirSpec ); /* can be NULL */
242
243static OSErr ProcessFolderList ( FSCopyFolderGlobals *folderGlobals );
244
245static OSErr CopyFolder ( FSCopyFolderGlobals *folderGlobals );
246
247static OSErr CheckForDestInsideSrc ( const FSRef *source,
248 const FSRef *destDir );
249
250static OSErr CopyForks ( const FSRef *source,
251 const FSRef *dest,
252 CopyParams *params );
253
254static OSErr CopyForksToDisk ( const FSRef *source,
255 const FSRef *dest,
256 CopyParams *params );
257
258static OSErr CopyForksToDropBox ( const FSRef *source,
259 const FSRef *dest,
260 CopyParams *params );
261
262static OSErr OpenAllForks ( const FSRef *dest,
263 GenLinkedList *forkList );
264
265static OSErr WriteFork ( const SInt16 srcRefNum,
266 const SInt16 destRefNum,
267 const CopyParams *params,
268 const SInt64 forkSize );
269
270static UInt32 CalcBufferSizeForVol ( const GetVolParmsInfoBuffer *volParms,
271 UInt32 volParmsSize );
272
273static UInt32 BufferSizeForVolSpeed ( UInt32 volumeBytesPerSecond );
274
275static OSErr FSDeleteFolder ( const FSRef *container );
276
277static void FSDeleteFolderLevel ( const FSRef *container,
278 FSDeleteObjectGlobals *theGlobals );
279
280static OSErr IsDropBox ( const FSRef *source,
281 Boolean *isDropBox );
282
283static OSErr GetMagicBusyCreateDate( UTCDateTime *date );
284
285static OSErr FSGetVRefNum ( const FSRef *ref,
286 FSVolumeRefNum *vRefNum );
287
288static OSErr FSGetVolParms ( FSVolumeRefNum volRefNum,
289 UInt32 bufferSize,
290 GetVolParmsInfoBuffer*volParmsInfo,
291 UInt32 *actualInfoSize ); /* Can Be NULL */
292
293static OSErr UniStrToPStr ( const HFSUniStr255 *uniStr,
294 TextEncoding textEncodingHint,
295 Boolean isVolumeName,
296 Str255 pStr );
297
298static OSErr FSMakeFSRef ( FSVolumeRefNum volRefNum,
299 SInt32 dirID,
300 ConstStr255Param name,
301 FSRef *ref );
302
303static OSErr SetupDestination ( const FSRef *destDir,
304 const DupeAction dupeAction,
305 HFSUniStr255 *sourceName,
306 FSRef *deleteMeRef,
307 Boolean *isReplacing);
308
309static OSErr GetUniqueName ( const FSRef *destDir,
310 HFSUniStr255 *sourceName );
311
312static OSErr GetObjectName ( const FSRef *sourceRef,
313 HFSUniStr255 *sourceName,
314 TextEncoding *sourceEncoding );
315
316static OSErr CreateFolder ( const FSRef *sourceRef,
317 const FSRef *destDirRef,
318 const FSCatalogInfo *catalogInfo,
319 const HFSUniStr255 *folderName,
320 CopyParams *params,
321 FSRef *newFSRefPtr,
322 FSSpec *newFSSpecPtr );
323
324static OSErr DoCreateFolder ( const FSRef *sourceRef,
325 const FSRef *destDirRef,
326 const FSCatalogInfo *catalogInfo,
327 const HFSUniStr255 *folderName,
328 CopyParams *params,
329 FSRef *newFSRefPtr,
330 FSSpec *newFSSpecPtr);
331
332static pascal void MyDisposeDataProc ( void *pData );
333
334static pascal void MyCloseForkProc ( void *pData );
335
336/*****************************************************************************/
337/*****************************************************************************/
338/*****************************************************************************/
339
340#pragma mark ----- Copy Objects -----
341
342 /* This routine acts as the top level of the copy engine. */
343OSErr FSCopyObject( const FSRef *source,
344 const FSRef *destDir,
345 ItemCount maxLevels,
346 FSCatalogInfoBitmap whichInfo,
347 DupeAction dupeAction,
348 const HFSUniStr255 *newObjectName, /* can be NULL */
349 Boolean wantFSSpec,
350 Boolean wantName,
351 CopyObjectFilterProcPtr filterProcPtr, /* can be NULL */
352 void *yourDataPtr, /* can be NULL */
353 FSRef *newObjectRef, /* can be NULL */
354 FSSpec *newObjectSpec) /* can be NULL */
355{
356 CopyParams copyParams;
357 FilterParams filterParams;
358 FSCatalogInfo sourceCatInfo;
359 HFSUniStr255 sourceName,
360 tmpObjectName;
361 FSRef tmpObjectRef,
362 deleteMeRef;
363 Boolean isDirectory = false,
364 isReplacing = false;
365 OSErr err = ( source != NULL && destDir != NULL ) ? noErr : paramErr;
366
367 /* Zero out these two FSRefs in case an error occurs before or */
368 /* inside FSCopyObjectPreflight. Paranoia mainly... */
369 BlockZero( &deleteMeRef, sizeof( FSRef ) );
370 BlockZero( &tmpObjectRef, sizeof( FSRef ) );
371
372 /* setup filterParams */
373 filterParams.whichInfo = whichInfo;
374 filterParams.filterProcPtr = filterProcPtr;
375 filterParams.wantSpec = ( filterProcPtr && wantFSSpec ); /* only get this info if */
376 filterParams.wantName = ( filterProcPtr && wantName ); /* a filterProc is provied */
377 filterParams.yourDataPtr = yourDataPtr;
378
379 /* Get and store away the name of the source object */
380 /* and setup the initial name of the new object */
381 if( err == noErr )
382 err = GetObjectName( source, &sourceName, NULL );
383 if( err == noErr )
384 tmpObjectName = (newObjectName != NULL) ? *newObjectName : sourceName;
385
386 if( err == noErr ) /* preflight/prep the destination and our internal variables */
387 err = FSCopyObjectPreflight( source, destDir, dupeAction, &sourceCatInfo, &copyParams, &tmpObjectName, &deleteMeRef, &isReplacing, &isDirectory );
388
389 /* now that we have some info, lets print it */
390 if( err == noErr )
391 {
392 dwarning(( "%s -- err: %d, maxLevels: %u, whichInfo: %08x,\n", __FUNCTION__, err, (unsigned int)maxLevels, (int)whichInfo ));
393 dwarning(( "\t\t\t\tdupeAction: %s, wantSpec: %s, wantName: %s,\n", ((dupeAction == kDupeActionReplace) ? "replace" : ((dupeAction == kDupeActionRename) ? "rename" : "standard")), (filterParams.wantSpec)?"yes":"no", (filterParams.wantName)?"yes":"no" ));
394 dwarning(( "\t\t\t\tfilterProcPtr: 0x%08x, yourDataPtr: 0x%08x,\n", (unsigned int)filterProcPtr, (unsigned int)yourDataPtr ));
395 dwarning(( "\t\t\t\tnewObjectRef: 0x%08x, newObjectSpec: 0x%08x,\n", (unsigned int)newObjectRef, (unsigned int)newObjectSpec ));
396 dwarning(( "\t\t\t\tcopyBufferSize: %dkB, isDirectory: %s, isLocal: %s,\n", (int)copyParams.copyBufferSize/1024, (isDirectory)?"yes":"no", (copyParams.copyingToLocalVolume)?"yes":"no" ));
397 dwarning(( "\t\t\t\tisDropBox: %s, PBHCopyFileSync supported: %s\n\n", (copyParams.copyingToDropFolder)?"yes":"no", (copyParams.volHasCopyFile)?"yes":"no" ));
398 }
399
400 if( err == noErr ) /* now copy the file/folder... */
401 { /* is it a folder? */
402 if ( isDirectory )
403 { /* yes */
404 err = CheckForDestInsideSrc(source, destDir);
405 if( err == noErr )
406 err = FSCopyFolder( source, destDir, &sourceCatInfo, &tmpObjectName, &copyParams, &filterParams, maxLevels, &tmpObjectRef, newObjectSpec );
407 }
408 else /* no */
409 err = FSCopyFile(source, destDir, &sourceCatInfo, &tmpObjectName, &copyParams, &filterParams, &tmpObjectRef, newObjectSpec);
410 }
411
412 /* if an object existed in the destination with the same name as */
413 /* the source and the caller wants to replace it, we had renamed it */
414 /* to ".DeleteMe" earlier. If no errors, we delete it, else delete */
415 /* the one we just created and rename the origenal back to its */
416 /* origenal name. */
417 /* */
418 /* This is done mainly to cover the case of the source being in the */
419 /* destination directory when kDupeActionReplace is selected */
420 /* (3188701) */
421 if( copyParams.dupeAction == kDupeActionReplace && isReplacing == true )
422 {
423 dwarning(("%s -- Cleaning up, this might take a moment. err : %d\n", __FUNCTION__, err));
424
425 if( err == noErr )
426 err = FSDeleteObjects( &deleteMeRef );
427 else
428 { /* not much we can do if the delete or rename fails, we need to preserve */
429 /* the origenal error code that got us here. */
430 /* */
431 /* If an error occurs before or inside SetupDestination, newFileRef and */
432 /* deleteMeRef will be invalid so the delete and rename will simply fail */
433 /* leaving the source and destination unchanged */
434 myverify_noerr( FSDeleteObjects( &tmpObjectRef ) );
435 myverify_noerr( FSRenameUnicode( &deleteMeRef, sourceName.length, sourceName.unicode, sourceCatInfo.textEncodingHint, NULL ) );
436 }
437 }
438
439 if( err == noErr && newObjectRef != NULL )
440 *newObjectRef = tmpObjectRef;
441
442 /* Clean up for space and safety... Who me? */
443 if( copyParams.copyBuffer != NULL )
444 DisposePtr((char*)copyParams.copyBuffer);
445
446 mycheck_noerr( err );
447
448 return err;
449}
450
451/*****************************************************************************/
452
453 /* Does a little preflighting (as the name suggests) to figure out the optimal */
454 /* buffer size, if its a drop box, on a remote volume etc */
455static OSErr FSCopyObjectPreflight( const FSRef *source,
456 const FSRef *destDir,
457 const DupeAction dupeAction,
458 FSCatalogInfo *sourceCatInfo,
459 CopyParams *copyParams,
460 HFSUniStr255 *newObjectName,
461 FSRef *deleteMeRef,
462 Boolean *isReplacing,
463 Boolean *isDirectory)
464{
465 GetVolParmsInfoBuffer srcVolParms,
466 destVolParms;
467 UInt32 srcVolParmsSize = 0,
468 destVolParmsSize = 0;
469 FSVolumeRefNum srcVRefNum = 0,
470 destVRefNum = 0;
471 OSErr err = ( source != NULL && destDir != NULL &&
472 sourceCatInfo != NULL && copyParams != NULL &&
473 newObjectName != NULL && deleteMeRef != NULL &&
474 isDirectory != NULL ) ? noErr : paramErr;
475
476 BlockZero( copyParams, sizeof( CopyParams ) );
477
478 copyParams->dupeAction = dupeAction;
479
480 if( err == noErr ) /* Get the info we will need later about the source object */
481 err = FSGetCatalogInfo( source, kFSCatInfoSettableInfo, sourceCatInfo, NULL, NULL, NULL );
482 if( err == noErr ) /* get the source's vRefNum */
483 err = FSGetVRefNum( source, &srcVRefNum );
484 if( err == noErr ) /* get the source's volParams */
485 err = FSGetVolParms( srcVRefNum, sizeof(GetVolParmsInfoBuffer), &srcVolParms, &srcVolParmsSize );
486 if( err == noErr ) /* get the destination's vRefNum */
487 err = FSGetVRefNum( destDir, &destVRefNum );
488 if( err == noErr )
489 {
490 /* Calculate the optimal copy buffer size for the src vol */
491 copyParams->copyBufferSize = CalcBufferSizeForVol( &srcVolParms, srcVolParmsSize );
492
493 /* if src and dest on different volumes, get its vol parms */
494 /* and calculate its optimal buffer size */
495 /* else destVolParms = srcVolParms */
496 if( srcVRefNum != destVRefNum )
497 {
498 err = FSGetVolParms( destVRefNum, sizeof(GetVolParmsInfoBuffer), &destVolParms, &destVolParmsSize );
499 if( err == noErr )
500 {
501 ByteCount tmpBufferSize = CalcBufferSizeForVol( &destVolParms, destVolParmsSize );
502 if( tmpBufferSize < copyParams->copyBufferSize )
503 copyParams->copyBufferSize = tmpBufferSize;
504 }
505 }
506 else
507 destVolParms = srcVolParms;
508 }
509 if( err == noErr )
510 err = ((copyParams->copyBuffer = NewPtr( copyParams->copyBufferSize )) != NULL ) ? noErr : MemError();
511
512 /* figure out if source is a file or folder */
513 /* if it is on a local volume, */
514 /* if destination is a drop box */
515 /* if source and dest are on same server */
516 /* and if it supports PBHCopyFile */
517 if( err == noErr ) /* is the destination a Drop Box */
518 err = IsDropBox( destDir, &copyParams->copyingToDropFolder );
519 if( err == noErr )
520 {
521 /* Is it a directory */
522 *isDirectory = ((sourceCatInfo->nodeFlags & kFSNodeIsDirectoryMask) != 0);
523 /* destVolParms.vMServerAdr is non-zero for remote volumes */
524 copyParams->copyingToLocalVolume = (destVolParms.vMServerAdr == 0);
525 if( !copyParams->copyingToLocalVolume )
526 {
527 /* If the destination is on a remote volume, and source and dest are on */
528 /* the same server, then it might support PBHCopyFileSync */
529 /* If not, then PBHCopyFileSync won't work */
530
531 /* figure out if the volumes support PBHCopyFileSync */
532 copyParams->volHasCopyFile = ( err == noErr && destVolParms.vMServerAdr == srcVolParms.vMServerAdr ) ?
533 VolHasCopyFile(&srcVolParms) : false;
534 }
535 }
536
537 if( err == noErr )
538 err = SetupDestination( destDir, copyParams->dupeAction, newObjectName, deleteMeRef, isReplacing );
539
540 return err;
541}
542
543#pragma mark ----- Copy Files -----
544
545/*****************************************************************************/
546
547static OSErr FSCopyFile( const FSRef *source,
548 const FSRef *destDir,
549 const FSCatalogInfo *sourceCatInfo,
550 const HFSUniStr255 *newFileName,
551 CopyParams *copyParams,
552 FilterParams *filterParams,
553 FSRef *outFileRef,
554 FSSpec *outFileSpec )
555{
556 FSCatalogInfo catInfo = *sourceCatInfo;
557 FSRef newFileRef;
558 FSSpec newFileSpec;
559 OSErr err = ( source != NULL && destDir != NULL &&
560 copyParams != NULL && filterParams != NULL ) ? noErr : paramErr;
561
562 /* If you would like a Pre-Copy filter (i.e to weed out objects */
563 /* you don't want to copy) you should add it here */
564
565 if( err == noErr ) /* copy the file over */
566 err = CopyFile( source, &catInfo, destDir, newFileName, copyParams, &newFileRef, (filterParams->wantSpec || outFileSpec) ? &newFileSpec : NULL );
567
568 /* Call the IterateFilterProc _after_ the new file was created even if an error occured. */
569 /* Note: if an error occured above, the FSRef and other info might not be valid */
570 if( filterParams->filterProcPtr != NULL )
571 {
572 /* get the extra info the user wanted on the new file that we don't have */
573 if( err == noErr && (filterParams->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone )
574 err = FSGetCatalogInfo( &newFileRef, filterParams->whichInfo & ~kFSCatInfoSettableInfo, &catInfo, NULL, NULL, NULL );
575
576 err = CallCopyObjectFilterProc( filterParams->filterProcPtr, false, 0, err, &catInfo, &newFileRef,
577 (filterParams->wantSpec) ? &newFileSpec : NULL,
578 (filterParams->wantName) ? newFileName : NULL,
579 filterParams->yourDataPtr);
580 }
581
582 if( err == noErr )
583 {
584 if( outFileRef != NULL )
585 *outFileRef = newFileRef;
586 if( outFileSpec != NULL )
587 *outFileSpec = newFileSpec;
588 }
589
590 mycheck_noerr(err);
591
592 return err;
593}
594
595/*****************************************************************************/
596
597static OSErr CopyFile( const FSRef *source,
598 FSCatalogInfo *sourceCatInfo,
599 const FSRef *destDir,
600 const HFSUniStr255 *destName, /* can be NULL */
601 CopyParams *params,
602 FSRef *newFile, /* can be NULL */
603 FSSpec *newSpec ) /* can be NULL */
604{
605 OSErr err = paramErr;
606
607 /* Clear the "inited" bit so that the Finder positions the icon for us. */
608 ((FInfo *)(sourceCatInfo->finderInfo))->fdFlags &= ~kHasBeenInited;
609
610 /* if the volumes support PBHCopyFileSync, try to use it */
611 if( params->volHasCopyFile == true )
612 err = FSUsePBHCopyFile( source, destDir, destName, kTextEncodingUnknown, newFile, newSpec );
613
614 /* if PBHCopyFile didn't work or not supported, */
615 if( err != noErr ) /* then try old school file transfer */
616 err = DoCopyFile( source, sourceCatInfo, destDir, destName, params, newFile, newSpec );
617
618 mycheck_noerr(err);
619
620 return err;
621}
622
623/*****************************************************************************/
624
625 /* Wrapper function for PBHCopyFileSync */
626static OSErr FSUsePBHCopyFile( const FSRef *srcFileRef,
627 const FSRef *dstDirectoryRef,
628 const HFSUniStr255 *destName, /* can be NULL */
629 TextEncoding textEncodingHint,
630 FSRef *newRef, /* can be NULL */
631 FSSpec *newSpec) /* can be NULL */
632{
633 FSSpec srcFileSpec;
634 FSCatalogInfo catalogInfo;
635 HParamBlockRec pb;
636 Str255 hfsName;
637 OSErr err = ( srcFileRef != NULL && dstDirectoryRef != NULL ) ? noErr : paramErr;
638
639 if( err == noErr ) /* get FSSpec of source FSRef */
640 err = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL);
641 if( err == noErr ) /* get the destination vRefNum and nodeID (nodeID is the dirID) */
642 err = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume | kFSCatInfoNodeID, &catalogInfo, NULL, NULL, NULL);
643 if( err == noErr ) /* gather all the info needed */
644 {
645 pb.copyParam.ioVRefNum = srcFileSpec.vRefNum;
646 pb.copyParam.ioDirID = srcFileSpec.parID;
647 pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name;
648 pb.copyParam.ioDstVRefNum = catalogInfo.volume;
649 pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID;
650 pb.copyParam.ioNewName = NULL;
651 if( destName != NULL )
652 err = UniStrToPStr( destName, textEncodingHint, false, hfsName );
653 pb.copyParam.ioCopyName = ( destName != NULL && err == noErr ) ? hfsName : NULL;
654 }
655 if( err == noErr ) /* tell the server to copy the object */
656 err = PBHCopyFileSync(&pb);
657
658 if( err == noErr )
659 {
660 if( newSpec != NULL ) /* caller wants an FSSpec, so make it */
661 myverify_noerr(FSMakeFSSpec( pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, pb.copyParam.ioCopyName, newSpec));
662 if( newRef != NULL ) /* caller wants an FSRef, so make it */
663 myverify_noerr(FSMakeFSRef( pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, pb.copyParam.ioCopyName, newRef));
664 }
665
666 if( err != paramErr ) /* returning paramErr is ok, it means PBHCopyFileSync was not supported */
667 mycheck_noerr(err);
668
669 return err;
670}
671
672/*****************************************************************************/
673
674 /* Copies a file referenced by source to the directory referenced by */
675 /* destDir. destName is the name the file we are going to copy to the */
676 /* destination. sourceCatInfo is the catalog info of the file, which */
677 /* is passed in as an optimization (we could get it by doing a */
678 /* FSGetCatalogInfo but the caller has already done that so we might as */
679 /* well take advantage of that). */
680 /* */
681static OSErr DoCopyFile(const FSRef *source,
682 FSCatalogInfo *sourceCatInfo,
683 const FSRef *destDir,
684 const HFSUniStr255 *destName,
685 CopyParams *params,
686 FSRef *newRef,
687 FSSpec *newSpec )
688{
689 FSRef dest;
690 FSSpec tmpSpec;
691 FSPermissionInfo originalPermissions;
692 OSType originalFileType = 'xxxx';
693 UInt16 originalNodeFlags = kFSCatInfoNone;
694 Boolean getSpec;
695 OSErr err = noErr;
696
697 /* If we're copying to a drop folder, we won't be able to reset this */
698 /* information once the copy is done, so we don't mess it up in */
699 /* the first place. We still clear the locked bit though; items dropped */
700 /* into a drop folder always become unlocked. */
701 if (!params->copyingToDropFolder)
702 {
703 /* Remember to clear the file's type, so the Finder doesn't */
704 /* look at the file until we're done. */
705 originalFileType = ((FInfo *) &sourceCatInfo->finderInfo)->fdType;
706 ((FInfo *) &sourceCatInfo->finderInfo)->fdType = kFirstMagicBusyFiletype;
707
708 /* Remember and clear the file's locked status, so that we can */
709 /* actually write the forks we're about to create. */
710 originalNodeFlags = sourceCatInfo->nodeFlags;
711 }
712 sourceCatInfo->nodeFlags &= ~kFSNodeLockedMask;
713
714 /* figure out if we should get the FSSpec to the new file or not */
715 /* If the caller asked for it, or if we need it for symlinks */
716 getSpec = ( ( newSpec != NULL ) || ( !params->copyingToDropFolder && originalFileType == 'slnk' && ((FInfo *) &sourceCatInfo->finderInfo)->fdCreator == 'rhap' ) );
717
718 /* we need to have user level read/write/execute access to the file we are */
719 /* going to create otherwise FSCreateFileUnicode will return */
720 /* -5000 (afpAccessDenied), and the FSRef returned will be invalid, yet */
721 /* the file is created (size 0k)... bug? */
722 originalPermissions = *((FSPermissionInfo*)sourceCatInfo->permissions);
723 ((FSPermissionInfo*)sourceCatInfo->permissions)->mode |= kRWXUserAccessMask;
724
725 /* Classic only supports 9.1 and higher, so we don't have to worry */
726 /* about 2397324 */
727 if( err == noErr )
728 err = FSCreateFileUnicode(destDir, destName->length, destName->unicode, kFSCatInfoSettableInfo, sourceCatInfo, &dest, ( getSpec ) ? &tmpSpec : NULL );
729 if( err == noErr ) /* Copy the forks over to the new file */
730 err = CopyForks(source, &dest, params);
731
732 /* Restore the original file type, creation and modification dates, */
733 /* locked status and permissions. */
734 /* This is one of the places where we need to handle drop */
735 /* folders as a special case because this FSSetCatalogInfo will fail for */
736 /* an item in a drop folder, so we don't even attempt it. */
737 if (err == noErr && !params->copyingToDropFolder)
738 {
739 ((FInfo *) &sourceCatInfo->finderInfo)->fdType = originalFileType;
740 sourceCatInfo->nodeFlags = originalNodeFlags;
741 *((FSPermissionInfo*)sourceCatInfo->permissions) = originalPermissions;
742
743 /* 2796751, FSSetCatalogInfo returns -36 when setting the Finder Info */
744 /* for a symlink. To workaround this, when the file is a */
745 /* symlink (slnk/rhap) we will finish the copy in two steps. First */
746 /* setting everything but the Finder Info on the file, then calling */
747 /* FSpSetFInfo to set the Finder Info for the file. I would rather use */
748 /* an FSRef function to set the Finder Info, but FSSetCatalogInfo is */
749 /* the only one... catch-22... */
750 /* */
751 /* The Carbon File Manager always sets the type/creator of a symlink to */
752 /* slnk/rhap if the file is a symlink we do the two step, if it isn't */
753 /* we use FSSetCatalogInfo to do all the work. */
754 if ((originalFileType == 'slnk') && (((FInfo *) &sourceCatInfo->finderInfo)->fdCreator == 'rhap'))
755 { /* Its a symlink */
756 /* set all the info, except the Finder info */
757 err = FSSetCatalogInfo(&dest, kFSCatInfoNodeFlags | kFSCatInfoPermissions, sourceCatInfo);
758 if ( err == noErr ) /* set the Finder Info to that file */
759 err = FSpSetFInfo( &tmpSpec, ((FInfo *) &sourceCatInfo->finderInfo) );
760 }
761 else /* its a regular file */
762 err = FSSetCatalogInfo(&dest, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo | kFSCatInfoPermissions, sourceCatInfo);
763 }
764
765 /* If we created the file and the copy failed, try to clean up by */
766 /* deleting the file we created. We do this because, while it's */
767 /* possible for the copy to fail halfway through and the File Manager */
768 /* doesn't really clean up that well in that case, we *really* don't want */
769 /* any half-created files being left around. */
770 /* if the file already existed, we don't want to delete it */
771 if( err == noErr || err == dupFNErr )
772 { /* if everything was fine, then return the new file Spec/Ref */
773 if( newRef != NULL )
774 *newRef = dest;
775 if( newSpec != NULL )
776 *newSpec = tmpSpec;
777 }
778 else
779 myverify_noerr( FSDeleteObjects(&dest) );
780
781 mycheck_noerr(err);
782
783 return err;
784}
785
786/*****************************************************************************/
787
788#pragma mark ----- Copy Folders -----
789
790static OSErr FSCopyFolder( const FSRef *source,
791 const FSRef *destDir,
792 const FSCatalogInfo *sourceCatInfo,
793 const HFSUniStr255 *newObjectName,
794 CopyParams *copyParams,
795 FilterParams *filterParams,
796 ItemCount maxLevels,
797 FSRef *outDirRef,
798 FSSpec *outDirSpec )
799{
800 FSCopyFolderGlobals folderGlobals;
801 FolderListData *tmpListData = NULL;
802 FSCatalogInfo catInfo = *sourceCatInfo;
803 FSRef newDirRef;
804 FSSpec newDirSpec;
805 OSErr err;
806
807 /* setup folder globals */
808 folderGlobals.catInfoList = (FSCatalogInfo*) NewPtr( sizeof( FSCatalogInfo ) * kNumObjects );
809 folderGlobals.srcRefList = (FSRef*) NewPtr( sizeof( FSRef ) * kNumObjects );
810 folderGlobals.nameList = (HFSUniStr255*) NewPtr( sizeof( HFSUniStr255 ) * kNumObjects );
811 folderGlobals.folderListIter = NULL;
812 folderGlobals.copyParams = copyParams;
813 folderGlobals.filterParams = filterParams;
814 folderGlobals.maxLevels = maxLevels;
815 folderGlobals.currentLevel = 0;
816
817 /* if any of the NewPtr calls failed, we MUST bail */
818 err = ( folderGlobals.catInfoList != NULL &&
819 folderGlobals.srcRefList != NULL &&
820 folderGlobals.nameList != NULL ) ? noErr : memFullErr;
821
822 /* init the linked list we will use to keep track of the folders */
823 InitLinkedList( &folderGlobals.folderList, MyDisposeDataProc );
824
825 if( err == noErr && !copyParams->copyingToDropFolder )
826 err = GetMagicBusyCreateDate( &catInfo.createDate );
827 if( err == noErr ) /* create the directory */
828 err = DoCreateFolder( source, destDir, &catInfo, newObjectName, folderGlobals.copyParams, &newDirRef, (filterParams->wantSpec || outDirSpec ) ? &newDirSpec : NULL );
829
830 /* Note: if an error occured above, the FSRef and other info might not be valid */
831 if( filterParams->filterProcPtr != NULL )
832 {
833 /* get the info the user wanted about the source directory we don't have */
834 if( err == noErr && (filterParams->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone )
835 err = FSGetCatalogInfo(&newDirRef, filterParams->whichInfo & ~kFSCatInfoSettableInfo, &catInfo, NULL, NULL, NULL);
836
837 err = CallCopyObjectFilterProc(filterParams->filterProcPtr, false, folderGlobals.currentLevel,
838 err, &catInfo, &newDirRef,
839 ( filterParams->wantSpec ) ? &newDirSpec : NULL,
840 ( filterParams->wantName ) ? newObjectName : NULL,
841 filterParams->yourDataPtr);
842 }
843 if( err == noErr ) /* create the memory for this folder */
844 err = ( ( tmpListData = (FolderListData*) NewPtr( sizeof( FolderListData ) ) ) != NULL ) ? noErr : MemError();
845 if( err == noErr )
846 { /* setup the folder info */
847 tmpListData->sourceDirRef = *source;
848 tmpListData->destDirRef = newDirRef;
849 tmpListData->level = folderGlobals.currentLevel;
850 /* add this folder to the list to give ProcessFolderList something to chew on */
851 err = AddToTail( &folderGlobals.folderList, tmpListData );
852 if( err == noErr ) /* tmpListData added successfully */
853 err = ProcessFolderList( &folderGlobals );
854 else /* error occured, so dispose of memory */
855 DisposePtr( (char*) tmpListData );
856 }
857
858 dwarning(("\n%s -- %u folders were found\n", __FUNCTION__, (unsigned int)GetNumberOfItems( &folderGlobals.folderList ) ));
859
860 /* when we're done destroy the list and free up any memory we allocated */
861 DestroyList( &folderGlobals.folderList );
862
863 /* now that the copy is complete, we can set things back to normal */
864 /* for the directory we just created. */
865 /* We have to do this only for the top directory of the copy */
866 /* all subdirectories were created all at once */
867 if( err == noErr && !folderGlobals.copyParams->copyingToDropFolder )
868 err = FSSetCatalogInfo( &newDirRef, kFSCatInfoCreateDate | kFSCatInfoPermissions, sourceCatInfo );
869
870 /* Copy went as planned, and caller wants an FSRef/FSSpec to the new directory */
871 if( err == noErr )
872 {
873 if( outDirRef != NULL)
874 *outDirRef = newDirRef;
875 if( outDirSpec != NULL )
876 *outDirSpec = newDirSpec;
877 }
878
879 /* clean up for space and safety, who me? */
880 if( folderGlobals.catInfoList )
881 DisposePtr( (char*) folderGlobals.catInfoList );
882 if( folderGlobals.srcRefList )
883 DisposePtr( (char*) folderGlobals.srcRefList );
884 if( folderGlobals.nameList )
885 DisposePtr( (char*) folderGlobals.nameList );
886
887 mycheck_noerr(err);
888
889 return ( err );
890}
891
892/*****************************************************************************/
893
894 /* We now store a list of all the folders/subfolders we encounter in the source */
895 /* Each node in the list contains an FSRef to the source, an FSRef to the */
896 /* mirror folder in the destination, and the level in the source that folder */
897 /* is on. This is done so that we can use FSGetCatalogInfoBulk to its full */
898 /* potential (getting items in bulk). We copy the source one folder at a time. */
899 /* Copying over the contents of each folder before we continue on to the next */
900 /* folder in the list. This allows us to use the File Manager's own caching */
901 /* system to our advantage. */
902static OSErr ProcessFolderList( FSCopyFolderGlobals *folderGlobals )
903{
904 FolderListData *folderListData;
905 OSErr err = noErr;
906
907 /* iterate through the list of folders and copy over each one individually */
908 for( InitIterator( &folderGlobals->folderList, &folderGlobals->folderListIter ); folderGlobals->folderListIter != NULL && err == noErr; Next( &folderGlobals->folderListIter ) )
909 {
910 /* Get the data for this folder */
911 folderListData = (FolderListData*) GetData( folderGlobals->folderListIter );
912 if( folderListData != NULL )
913 {
914 #if DEBUG && !TARGET_API_MAC_OS8
915 {
916 char path[1024];
917 myverify_noerr(FSRefMakePath( &(folderListData->sourceDirRef), (unsigned char*)path, 1024 ));
918 dwarning(("\n\n%s -- Copying contents of\n\t%s\n", __FUNCTION__, path));
919 myverify_noerr(FSRefMakePath( &(folderListData->destDirRef), (unsigned char*)path, 1024 ));
920 dwarning(("\t\tto\n\t%s\n", path));
921 }
922 #endif
923
924 /* stuff the data into our globals */
925 folderGlobals->sourceDirRef = &(folderListData->sourceDirRef);
926 folderGlobals->destDirRef = &(folderListData->destDirRef);
927 folderGlobals->currentLevel = folderListData->level;
928
929 /* Copy over this folder and add any subfolders to our list of folders */
930 /* so they will get processed later */
931 err = CopyFolder( folderGlobals );
932 }
933 }
934
935 return err;
936}
937
938/*****************************************************************************/
939
940 /* Copy the contents of the source into the destination. If any subfolders */
941 /* are found, add them to a local list of folders during the loop stage */
942 /* Once the copy is done, insert the local list into the global list right */
943 /* after the current position in the list. This is done so we don't jump */
944 /* all over the disk getting the different folders to copy */
945static OSErr CopyFolder( FSCopyFolderGlobals *folderGlobals )
946{
947 GenLinkedList tmpList;
948 FolderListData *tmpListData = NULL;
949 FilterParams *filterPtr = folderGlobals->filterParams;
950 FSIterator iterator;
951 FSRef newRef;
952 FSSpec newSpec;
953 UInt32 actualObjects;
954 OSErr err,
955 junkErr;
956 int i;
957
958 /* Init the local list */
959 InitLinkedList( &tmpList, MyDisposeDataProc);
960
961 err = FSOpenIterator( folderGlobals->sourceDirRef, kFSIterateFlat, &iterator );
962 if( err == noErr )
963 {
964 do
965 {
966 /* grab a bunch of objects (kNumObjects) from this folder and copy them over */
967 err = FSGetCatalogInfoBulk( iterator, kNumObjects, &actualObjects, &filterPtr->containerChanged,
968 kFSCatInfoSettableInfo, folderGlobals->catInfoList, folderGlobals->srcRefList,
969 NULL, folderGlobals->nameList );
970 if( ( err == noErr || err == errFSNoMoreItems ) &&
971 ( actualObjects != 0 ) )
972 {
973 dwarning(("%s -- actualObjects retrieved from FSGetCatalogInfoBulk: %u\n",__FUNCTION__, (unsigned int)actualObjects ));
974
975 /* iterate over the objects actually returned */
976 for( i = 0; i < actualObjects; i++ )
977 {
978 /* Any errors in here will be passed to the filter proc */
979 /* we don't want an error in here to prematurely cancel the copy */
980
981 /* If you would like a Pre-Copy filter (i.e to weed out objects */
982 /* you don't want to copy) you should add it here */
983
984 /* Is the new object a directory? */
985 if( ( folderGlobals->catInfoList[i].nodeFlags & kFSNodeIsDirectoryMask ) != 0 )
986 { /* yes */
987 junkErr = CreateFolder( &folderGlobals->srcRefList[i], folderGlobals->destDirRef,
988 &folderGlobals->catInfoList[i], &folderGlobals->nameList[i],
989 folderGlobals->copyParams, &newRef, (filterPtr->wantSpec) ? &newSpec : NULL );
990 /* If maxLevels is zero, we aren't checking levels */
991 /* If currentLevel+1 < maxLevels, add this folder to the list */
992 if( folderGlobals->maxLevels == 0 || (folderGlobals->currentLevel + 1) < folderGlobals->maxLevels )
993 {
994 if( junkErr == noErr ) /* Create memory for folder list data */
995 junkErr = ( ( tmpListData = (FolderListData*) NewPtr( sizeof( FolderListData ) ) ) != NULL ) ? noErr : MemError();
996 if( junkErr == noErr )
997 { /* Setup the folder list data */
998 tmpListData->sourceDirRef = folderGlobals->srcRefList[i];
999 tmpListData->destDirRef = newRef;
1000 tmpListData->level = folderGlobals->currentLevel + 1;
1001
1002 /* Add it to the local list */
1003 junkErr = AddToTail( &tmpList, tmpListData );
1004 }
1005 /* If an error occured and memory was created, we need to dispose of it */
1006 /* since it was not added to the list */
1007 if( junkErr != noErr && tmpListData != NULL )
1008 DisposePtr( (char*) tmpListData );
1009 }
1010 }
1011 else
1012 { /* no */
1013 junkErr = CopyFile( &folderGlobals->srcRefList[i], &folderGlobals->catInfoList[i],
1014 folderGlobals->destDirRef, &folderGlobals->nameList[i],
1015 folderGlobals->copyParams, &newRef, ( filterPtr->wantSpec ) ? &newSpec : NULL );
1016 }
1017
1018 /* Note: if an error occured above, the FSRef and other info might not be valid */
1019 if( filterPtr->filterProcPtr != NULL )
1020 {
1021 if( junkErr == noErr && (filterPtr->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone ) /* get the extra info about the new object that the user wanted that we don't already have */
1022 junkErr = FSGetCatalogInfo( &newRef, filterPtr->whichInfo & ~kFSCatInfoSettableInfo, &folderGlobals->catInfoList[i], NULL, NULL, NULL );
1023
1024 err = CallCopyObjectFilterProc( filterPtr->filterProcPtr, filterPtr->containerChanged,
1025 folderGlobals->currentLevel, junkErr,
1026 &folderGlobals->catInfoList[i], &newRef,
1027 ( filterPtr->wantSpec ) ? &newSpec : NULL,
1028 ( filterPtr->wantName ) ? &folderGlobals->nameList[i] : NULL,
1029 filterPtr->yourDataPtr);
1030 }
1031 }
1032 }
1033 }while( err == noErr );
1034
1035 /* errFSNoMoreItems is OK - it only means we hit the end of this level */
1036 /* afpAccessDenied is OK too - it only means we cannot see inside the directory */
1037 if( err == errFSNoMoreItems || err == afpAccessDenied )
1038 err = noErr;
1039
1040 /* Insert the local list of folders from the current folder into our global list. Even */
1041 /* if no items were added to the local list (due to error, or empty folder), InsertList */
1042 /* handles it correctly. We add the local list even if an error occurred. It will get */
1043 /* disposed of when the global list is destroyed. Doesn't hurt to have a couple extra */
1044 /* steps when we're going to bail anyways. */
1045 InsertList( &folderGlobals->folderList, &tmpList, folderGlobals->folderListIter );
1046
1047 /* Close the FSIterator (closing an open iterator should never fail) */
1048 (void) FSCloseIterator(iterator);
1049 }
1050
1051 mycheck_noerr( err );
1052
1053 return err;
1054}
1055
1056/*****************************************************************************/
1057
1058 /* Determines whether the destination directory is equal to the source */
1059 /* item, or whether it's nested inside the source item. Returns a */
1060 /* errFSDestInsideSource if that's the case. We do this to prevent */
1061 /* endless recursion while copying. */
1062 /* */
1063static OSErr CheckForDestInsideSrc( const FSRef *source,
1064 const FSRef *destDir)
1065{
1066 FSRef thisDir = *destDir;
1067 FSCatalogInfo thisDirInfo;
1068 Boolean done = false;
1069 OSErr err;
1070
1071 do
1072 {
1073 err = FSCompareFSRefs(source, &thisDir);
1074 if (err == noErr)
1075 err = errFSDestInsideSource;
1076 else if (err == diffVolErr)
1077 {
1078 err = noErr;
1079 done = true;
1080 }
1081 else if (err == errFSRefsDifferent)
1082 {
1083 /* This is somewhat tricky. We can ask for the parent of thisDir */
1084 /* by setting the parentRef parameter to FSGetCatalogInfo but, if */
1085 /* thisDir is the volume's FSRef, this will give us back junk. */
1086 /* So we also ask for the parent's dir ID to be returned in the */
1087 /* FSCatalogInfo record, and then check that against the node */
1088 /* ID of the root's parent (ie 1). If we match that, we've made */
1089 /* it to the top of the hierarchy without hitting source, so */
1090 /* we leave with no error. */
1091
1092 err = FSGetCatalogInfo(&thisDir, kFSCatInfoParentDirID, &thisDirInfo, NULL, NULL, &thisDir);
1093 if( ( err == noErr ) && ( thisDirInfo.parentDirID == fsRtParID ) )
1094 done = true;
1095 }
1096 } while ( err == noErr && ! done );
1097
1098 mycheck_noerr( err );
1099
1100 return err;
1101}
1102
1103/*****************************************************************************/
1104
1105#pragma mark ----- Copy Forks -----
1106
1107 /* This is where the majority of the work is done. I special cased */
1108 /* DropBoxes in order to use FSIterateForks to its full potential for */
1109 /* the more common case (read/write permissions). It also simplifies */
1110 /* the code to have it seperate. */
1111static OSErr CopyForks( const FSRef *source,
1112 const FSRef *dest,
1113 CopyParams *params)
1114{
1115 OSErr err;
1116
1117 err = ( !params->copyingToDropFolder ) ? CopyForksToDisk ( source, dest, params ) :
1118 CopyForksToDropBox ( source, dest, params );
1119
1120 mycheck_noerr( err );
1121
1122 return err;
1123}
1124
1125 /* Open each fork individually and copy them over to the destination */
1126static OSErr CopyForksToDisk( const FSRef *source,
1127 const FSRef *dest,
1128 CopyParams *params )
1129{
1130 HFSUniStr255 forkName;
1131 CatPositionRec iterator;
1132 SInt64 forkSize;
1133 SInt16 srcRefNum,
1134 destRefNum;
1135 OSErr err;
1136
1137 /* need to initialize the iterator before using it */
1138 iterator.initialize = 0;
1139
1140 do
1141 {
1142 err = FSIterateForks( source, &iterator, &forkName, &forkSize, NULL );
1143
1144 /* Create the fork. Note: Data and Resource forks are automatically */
1145 /* created when the file is created. FSCreateFork returns noErr for them */
1146 /* We also want to create the fork even if there is no data to preserve */
1147 /* empty forks */
1148 if( err == noErr )
1149 err = FSCreateFork( dest, forkName.length, forkName.unicode );
1150
1151 /* Mac OS 9.0 has a bug (in the AppleShare external file system, */
1152 /* I think) [2410374] that causes FSCreateFork to return an errFSForkExists */
1153 /* error even though the fork is empty. The following code swallows */
1154 /* the error (which is harmless) in that case. */
1155 if( err == errFSForkExists && !params->copyingToLocalVolume )
1156 err = noErr;
1157
1158 /* The remainder of this code only applies if there is actual data */
1159 /* in the source fork. */
1160
1161 if( err == noErr && forkSize > 0 )
1162 {
1163 destRefNum = srcRefNum = 0;
1164
1165 /* Open the destination fork */
1166 err = FSOpenFork(dest, forkName.length, forkName.unicode, fsWrPerm, &destRefNum);
1167 if( err == noErr ) /* Open the source fork */
1168 err = FSOpenFork(source, forkName.length, forkName.unicode, fsRdPerm, &srcRefNum);
1169 if( err == noErr ) /* Write the fork to disk */
1170 err = WriteFork( srcRefNum, destRefNum, params, forkSize );
1171
1172 if( destRefNum != 0 ) /* Close the destination fork */
1173 myverify_noerr( FSCloseFork( destRefNum ) );
1174 if( srcRefNum != 0 ) /* Close the source fork */
1175 myverify_noerr( FSCloseFork( srcRefNum ) );
1176 }
1177 }
1178 while( err == noErr );
1179
1180 if( err == errFSNoMoreItems )
1181 err = noErr;
1182
1183 mycheck_noerr( err );
1184
1185 return err;
1186}
1187
1188 /* If we're copying to a DropBox, we have to handle the copy process a little */
1189 /* differently then when we are copying to a regular folder. */
1190static OSErr CopyForksToDropBox( const FSRef *source,
1191 const FSRef *dest,
1192 CopyParams *params )
1193{
1194 GenLinkedList forkList;
1195 GenIteratorPtr pIter;
1196 ForkTrackerPtr forkPtr;
1197 SInt16 srcRefNum;
1198 OSErr err;
1199
1200 InitLinkedList( &forkList, MyCloseForkProc );
1201
1202 /* If we're copying into a drop folder, open up all of those forks. */
1203 /* We have to do this because once we've started writing to a fork */
1204 /* in a drop folder, we can't open any more forks. */
1205 err = OpenAllForks( dest, &forkList );
1206
1207 /* Copy each fork over to the destination */
1208 for( InitIterator( &forkList, &pIter ); pIter != NULL && err == noErr; Next( &pIter ) )
1209 {
1210 srcRefNum = 0;
1211 forkPtr = GetData( pIter );
1212 /* Open the source fork */
1213 err = FSOpenFork(source, forkPtr->forkName.length, forkPtr->forkName.unicode, fsRdPerm, &srcRefNum);
1214 if( err == noErr ) /* Write the data over */
1215 err = WriteFork( srcRefNum, forkPtr->forkDestRefNum, params, forkPtr->forkSize );
1216
1217 if( srcRefNum != 0 ) /* Close the source fork */
1218 myverify_noerr( FSCloseFork( srcRefNum ) );
1219 }
1220 /* we're done, so destroy the list even if an error occured */
1221 /* the DisposeDataProc will close any open forks */
1222 DestroyList( &forkList );
1223
1224 mycheck_noerr( err );
1225
1226 return err;
1227}
1228
1229/*****************************************************************************/
1230
1231 /* Create and open all the forks in the destination file. We need to do this when */
1232 /* we're copying into a drop folder, where you must open all the forks before starting */
1233 /* to write to any of them. */
1234 /* */
1235 /* IMPORTANT: If it fails, this routine won't close forks that opened successfully. */
1236 /* Make sure that the DisposeDataProc for the forkList closed any open forks */
1237 /* Or you close each one manually before destroying the list */
1238static OSErr OpenAllForks( const FSRef *dest,
1239 GenLinkedList *forkList )
1240{
1241 ForkTrackerPtr forkPtr;
1242 HFSUniStr255 forkName;
1243 CatPositionRec iterator;
1244 SInt64 forkSize;
1245 OSErr err = ( dest != NULL && forkList != NULL ) ? noErr : paramErr;
1246
1247 /* need to initialize the iterator before using it */
1248 iterator.initialize = 0;
1249
1250 /* Iterate over the list of forks */
1251 while( err == noErr )
1252 {
1253 forkPtr = NULL; /* init forkPtr */
1254
1255 err = FSIterateForks( dest, &iterator, &forkName, &forkSize, NULL );
1256 if( err == noErr )
1257 err = ( forkPtr = (ForkTrackerPtr) NewPtr( sizeof( ForkTracker ) ) ) != NULL ? noErr : MemError();
1258 if( err == noErr )
1259 {
1260 forkPtr->forkName = forkName;
1261 forkPtr->forkSize = forkSize;
1262 forkPtr->forkDestRefNum = 0;
1263
1264 /* Create the fork. Note: Data and Resource forks are automatically */
1265 /* created when the file is created. FSCreateFork returns noErr for them */
1266 /* We also want to create the fork even if there is no data to preserve */
1267 /* empty forks */
1268 err = FSCreateFork( dest, forkName.length, forkName.unicode );
1269
1270 /* Swallow afpAccessDenied because this operation causes the external file */
1271 /* system compatibility shim in Mac OS 9 to generate a GetCatInfo request */
1272 /* to the AppleShare external file system, which in turn causes an AFP */
1273 /* GetFileDirParms request on the wire, which the AFP server bounces with */
1274 /* afpAccessDenied because the file is in a drop folder. As there's no */
1275 /* native support for non-classic forks in current AFP, there's no way I */
1276 /* can decide how I should handle this in a non-test case. So I just */
1277 /* swallow the error and hope that when native AFP support arrives, the */
1278 /* right thing will happen. */
1279 if( err == afpAccessDenied )
1280 err = noErr;
1281
1282 /* only open the fork if the fork has some data */
1283 if( err == noErr && forkPtr->forkSize > 0 )
1284 err = FSOpenFork( dest, forkPtr->forkName.length, forkPtr->forkName.unicode, fsWrPerm, &forkPtr->forkDestRefNum );
1285
1286 /* if everything is ok, add this fork to the list */
1287 if( err == noErr )
1288 err = AddToTail( forkList, forkPtr );
1289 }
1290
1291 if( err != noErr && forkPtr != NULL )
1292 DisposePtr( (char*) forkPtr );
1293 }
1294
1295 if( err == errFSNoMoreItems )
1296 err = noErr;
1297
1298 mycheck_noerr( err );
1299
1300 return err;
1301}
1302
1303/*****************************************************************************/
1304
1305 /* Writes the fork from the source, references by srcRefNum, to the destination fork */
1306 /* references by destRefNum */
1307static OSErr WriteFork( const SInt16 srcRefNum,
1308 const SInt16 destRefNum,
1309 const CopyParams *params,
1310 const SInt64 forkSize )
1311{
1312 UInt64 bytesRemaining;
1313 UInt64 bytesToReadThisTime;
1314 UInt64 bytesToWriteThisTime;
1315 OSErr err;
1316
1317
1318 /* Here we create space for the entire fork on the destination volume. */
1319 /* FSAllocateFork has the right semantics on both traditional Mac OS */
1320 /* and Mac OS X. On traditional Mac OS it will allocate space for the */
1321 /* file in one hit without any other special action. On Mac OS X, */
1322 /* FSAllocateFork is preferable to FSSetForkSize because it prevents */
1323 /* the system from zero filling the bytes that were added to the end */
1324 /* of the fork (which would be waste because we're about to write over */
1325 /* those bytes anyway. */
1326 err = FSAllocateFork(destRefNum, kFSAllocNoRoundUpMask, fsFromStart, 0, forkSize, NULL);
1327
1328 /* Copy the file from the source to the destination in chunks of */
1329 /* no more than params->copyBufferSize bytes. This is fairly */
1330 /* boring code except for the bytesToReadThisTime/bytesToWriteThisTime */
1331 /* distinction. On the last chunk, we round bytesToWriteThisTime */
1332 /* up to the next 512 byte boundary and then, after we exit the loop, */
1333 /* we set the file's EOF back to the real location (if the fork size */
1334 /* is not a multiple of 512 bytes). */
1335 /* */
1336 /* This technique works around a 'bug' in the traditional Mac OS File Manager, */
1337 /* where the File Manager will put the last 512-byte block of a large write into */
1338 /* the cache (even if we specifically request no caching) if that block is not */
1339 /* full. If the block goes into the cache it will eventually have to be */
1340 /* flushed, which causes sub-optimal disk performance. */
1341 /* */
1342 /* This is only done if the destination volume is local. For a network */
1343 /* volume, it's better to just write the last bytes directly. */
1344 /* */
1345 /* This is extreme over-optimization given the other limits of this */
1346 /* sample, but I will hopefully get to the other limits eventually. */
1347 bytesRemaining = forkSize;
1348 while( err == noErr && bytesRemaining != 0 )
1349 {
1350 if( bytesRemaining > params->copyBufferSize )
1351 {
1352 bytesToReadThisTime = params->copyBufferSize;
1353 bytesToWriteThisTime = bytesToReadThisTime;
1354 }
1355 else
1356 {
1357 bytesToReadThisTime = bytesRemaining;
1358 bytesToWriteThisTime = ( params->copyingToLocalVolume ) ?
1359 ( (bytesRemaining + 0x01FF ) & ~0x01FF ) : bytesRemaining;
1360 }
1361
1362 err = FSReadFork( srcRefNum, fsAtMark + noCacheMask, 0, bytesToReadThisTime, params->copyBuffer, NULL );
1363 if( err == noErr )
1364 err = FSWriteFork( destRefNum, fsAtMark + noCacheMask, 0, bytesToWriteThisTime, params->copyBuffer, NULL );
1365 if( err == noErr )
1366 bytesRemaining -= bytesToReadThisTime;
1367 }
1368
1369 if (err == noErr && params->copyingToLocalVolume && ( forkSize & 0x01FF ) != 0 )
1370 err = FSSetForkSize( destRefNum, fsFromStart, forkSize );
1371
1372 return err;
1373}
1374
1375/*****************************************************************************/
1376
1377#pragma mark ----- Calculate Buffer Size -----
1378
1379 /* This routine calculates the appropriate buffer size for */
1380 /* the given volParms. It's a simple composition of FSGetVolParms */
1381 /* BufferSizeForVolSpeed. */
1382static UInt32 CalcBufferSizeForVol(const GetVolParmsInfoBuffer *volParms, UInt32 volParmsSize)
1383{
1384 UInt32 volumeBytesPerSecond = 0;
1385
1386 /* Version 1 of the GetVolParmsInfoBuffer included the vMAttrib */
1387 /* field, so we don't really need to test actualSize. A noErr */
1388 /* result indicates that we have the info we need. This is */
1389 /* just a paranoia check. */
1390
1391 mycheck(volParmsSize >= offsetof(GetVolParmsInfoBuffer, vMVolumeGrade));
1392
1393 /* On the other hand, vMVolumeGrade was not introduced until */
1394 /* version 2 of the GetVolParmsInfoBuffer, so we have to explicitly */
1395 /* test whether we got a useful value. */
1396
1397 if( ( volParmsSize >= offsetof(GetVolParmsInfoBuffer, vMForeignPrivID) ) &&
1398 ( volParms->vMVolumeGrade <= 0 ) )
1399 {
1400 volumeBytesPerSecond = -volParms->vMVolumeGrade;
1401 }
1402
1403 return BufferSizeForVolSpeed(volumeBytesPerSecond);
1404}
1405
1406/*****************************************************************************/
1407
1408 /* Calculate an appropriate copy buffer size based on the volumes */
1409 /* rated speed. Our target is to use a buffer that takes 0.25 */
1410 /* seconds to fill. This is necessary because the volume might be */
1411 /* mounted over a very slow link (like ARA), and if we do a 256 KB */
1412 /* read over an ARA link we'll block the File Manager queue for */
1413 /* so long that other clients (who might have innocently just */
1414 /* called PBGetCatInfoSync) will block for a noticeable amount of time. */
1415 /* */
1416 /* Note that volumeBytesPerSecond might be 0, in which case we assume */
1417 /* some default value. */
1418static UInt32 BufferSizeForVolSpeed(UInt32 volumeBytesPerSecond)
1419{
1420 ByteCount bufferSize;
1421
1422 if (volumeBytesPerSecond == 0)
1423 bufferSize = kDefaultCopyBufferSize;
1424 else
1425 { /* We want to issue a single read that takes 0.25 of a second, */
1426 /* so devide the bytes per second by 4. */
1427 bufferSize = volumeBytesPerSecond / 4;
1428 }
1429
1430 /* Round bufferSize down to 512 byte boundary. */
1431 bufferSize &= ~0x01FF;
1432
1433 /* Clip to sensible limits. */
1434 if (bufferSize < kMinimumCopyBufferSize)
1435 bufferSize = kMinimumCopyBufferSize;
1436 else if (bufferSize > kMaximumCopyBufferSize)
1437 bufferSize = kMaximumCopyBufferSize;
1438
1439 return bufferSize;
1440}
1441
1442/*****************************************************************************/
1443
1444#pragma mark ----- Delete Objects -----
1445
1446OSErr FSDeleteObjects( const FSRef *source )
1447{
1448 FSCatalogInfo catalogInfo;
1449 OSErr err = ( source != NULL ) ? noErr : paramErr;
1450
1451 #if DEBUG && !TARGET_API_MAC_OS8
1452 if( err == noErr )
1453 {
1454 char path[1024];
1455 myverify_noerr(FSRefMakePath( source, (unsigned char*)path, 1024 ));
1456 dwarning(("\n%s -- Deleting %s\n", __FUNCTION__, path));
1457 }
1458 #endif
1459
1460 /* get nodeFlags for container */
1461 if( err == noErr )
1462 err = FSGetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL,NULL);
1463 if( err == noErr && (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0 )
1464 { /* its a directory, so delete its contents before we delete it */
1465 err = FSDeleteFolder(source);
1466 }
1467 if( err == noErr && (catalogInfo.nodeFlags & kFSNodeLockedMask) != 0 ) /* is object locked? */
1468 { /* then attempt to unlock the object (ignore err since FSDeleteObject will set it correctly) */
1469 catalogInfo.nodeFlags &= ~kFSNodeLockedMask;
1470 (void) FSSetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo);
1471 }
1472 if( err == noErr ) /* delete the object (if it was a directory it is now empty, so we can delete it) */
1473 err = FSDeleteObject(source);
1474
1475 mycheck_noerr( err );
1476
1477 return ( err );
1478}
1479
1480/*****************************************************************************/
1481
1482#pragma mark ----- Delete Folders -----
1483
1484static OSErr FSDeleteFolder( const FSRef *container )
1485{
1486 FSDeleteObjectGlobals theGlobals;
1487
1488 theGlobals.result = ( container != NULL ) ? noErr : paramErr;
1489
1490 /* delete container's contents */
1491 if( theGlobals.result == noErr )
1492 FSDeleteFolderLevel(container, &theGlobals);
1493
1494 mycheck_noerr( theGlobals.result );
1495
1496 return ( theGlobals.result );
1497}
1498
1499/*****************************************************************************/
1500
1501static void FSDeleteFolderLevel(const FSRef *container,
1502 FSDeleteObjectGlobals *theGlobals )
1503{
1504 FSIterator iterator;
1505 FSRef itemToDelete;
1506 UInt16 nodeFlags;
1507
1508 /* Open FSIterator for flat access and give delete optimization hint */
1509 theGlobals->result = FSOpenIterator(container, kFSIterateFlat + kFSIterateDelete, &iterator);
1510 if ( theGlobals->result == noErr )
1511 {
1512 do /* delete the contents of the directory */
1513 {
1514 /* get 1 item to delete */
1515 theGlobals->result = FSGetCatalogInfoBulk( iterator, 1, &theGlobals->actualObjects,
1516 NULL, kFSCatInfoNodeFlags, &theGlobals->catalogInfo,
1517 &itemToDelete, NULL, NULL);
1518 if ( (theGlobals->result == noErr) && (theGlobals->actualObjects == 1) )
1519 {
1520 /* save node flags in local in case we have to recurse */
1521 nodeFlags = theGlobals->catalogInfo.nodeFlags;
1522
1523 /* is it a directory? */
1524 if ( (nodeFlags & kFSNodeIsDirectoryMask) != 0 )
1525 { /* yes -- delete its contents before attempting to delete it */
1526 FSDeleteFolderLevel(&itemToDelete, theGlobals);
1527 }
1528 if ( theGlobals->result == noErr) /* are we still OK to delete? */
1529 {
1530 if ( (nodeFlags & kFSNodeLockedMask) != 0 ) /* is item locked? */
1531 { /* then attempt to unlock it (ignore result since FSDeleteObject will set it correctly) */
1532 theGlobals->catalogInfo.nodeFlags = nodeFlags & ~kFSNodeLockedMask;
1533 (void) FSSetCatalogInfo(&itemToDelete, kFSCatInfoNodeFlags, &theGlobals->catalogInfo);
1534 }
1535 /* delete the item */
1536 theGlobals->result = FSDeleteObject(&itemToDelete);
1537 }
1538 }
1539 } while ( theGlobals->result == noErr );
1540
1541 /* we found the end of the items normally, so return noErr */
1542 if ( theGlobals->result == errFSNoMoreItems )
1543 theGlobals->result = noErr;
1544
1545 /* close the FSIterator (closing an open iterator should never fail) */
1546 myverify_noerr(FSCloseIterator(iterator));
1547 }
1548
1549 mycheck_noerr( theGlobals->result );
1550
1551 return;
1552}
1553
1554/*****************************************************************************/
1555
1556#pragma mark ----- Utilities -----
1557
1558 /* Figures out if the given directory is a drop box or not */
1559 /* if it is, the Copy Engine will behave slightly differently */
1560static OSErr IsDropBox( const FSRef* source,
1561 Boolean *isDropBox )
1562{
1563 FSCatalogInfo tmpCatInfo;
1564 FSSpec sourceSpec;
1565 Boolean isDrop = false;
1566 OSErr err;
1567
1568 /* get info about the destination, and an FSSpec to it for PBHGetDirAccess */
1569 err = FSGetCatalogInfo(source, kFSCatInfoNodeFlags | kFSCatInfoPermissions, &tmpCatInfo, NULL, &sourceSpec, NULL);
1570 if( err == noErr ) /* make sure the source is a directory */
1571 err = ((tmpCatInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) ? noErr : errFSNotAFolder;
1572 if( err == noErr )
1573 {
1574 HParamBlockRec hPB;
1575
1576 BlockZero( &hPB, sizeof( HParamBlockRec ) );
1577
1578 hPB.accessParam.ioNamePtr = sourceSpec.name;
1579 hPB.accessParam.ioVRefNum = sourceSpec.vRefNum;
1580 hPB.accessParam.ioDirID = sourceSpec.parID;
1581
1582 /* This is the official way (reads: the way X Finder does it) to figure */
1583 /* out the current users access privileges to a given directory */
1584 err = PBHGetDirAccessSync(&hPB);
1585 if( err == noErr ) /* its a drop folder if the current user only has write access */
1586 isDrop = (hPB.accessParam.ioACAccess & kPrivilegesMask) == kioACAccessUserWriteMask;
1587 else if ( err == paramErr )
1588 {
1589 /* There is a bug (2908703) in the Classic File System (not OS 9.x or Carbon) */
1590 /* on 10.1.x where PBHGetDirAccessSync sometimes returns paramErr even when the */
1591 /* data passed in is correct. This is a workaround/hack for that problem, */
1592 /* but is not as accurate. */
1593 /* Basically, if "Everyone" has only Write/Search access then its a drop folder */
1594 /* that is the most common case when its a drop folder */
1595 FSPermissionInfo *tmpPerm = (FSPermissionInfo *)tmpCatInfo.permissions;
1596 isDrop = ((tmpPerm->mode & kRWXOtherAccessMask) == kDropFolderValue);
1597 err = noErr;
1598 }
1599 }
1600
1601 *isDropBox = isDrop;
1602
1603 mycheck_noerr( err );
1604
1605 return err;
1606}
1607
1608/*****************************************************************************/
1609
1610 /* The copy engine is going to set the item's creation date */
1611 /* to kMagicBusyCreationDate while it's copying the item. */
1612 /* But kMagicBusyCreationDate is an old-style 32-bit date/time, */
1613 /* while the HFS Plus APIs use the new 64-bit date/time. So */
1614 /* we have to call a happy UTC utilities routine to convert from */
1615 /* the local time kMagicBusyCreationDate to a UTCDateTime */
1616 /* gMagicBusyCreationDate, which the File Manager will store */
1617 /* on disk and which the Finder we read back using the old */
1618 /* APIs, whereupon the File Manager will convert it back */
1619 /* to local time (and hopefully get the kMagicBusyCreationDate */
1620 /* back!). */
1621static OSErr GetMagicBusyCreateDate( UTCDateTime *date )
1622{
1623 static UTCDateTime magicDate = { 0, 0xDEADBEEF, 0 };
1624 OSErr err = ( date != NULL ) ? noErr : paramErr;
1625
1626 if( err == noErr && magicDate.lowSeconds == 0xDEADBEEF )
1627 err = ConvertLocalTimeToUTC( kMagicBusyCreationDate, &magicDate.lowSeconds );
1628 if( err == noErr )
1629 *date = magicDate;
1630
1631 mycheck_noerr( err );
1632
1633 return err;
1634}
1635
1636/*****************************************************************************/
1637
1638static OSErr FSGetVRefNum( const FSRef *ref,
1639 FSVolumeRefNum *vRefNum)
1640{
1641 FSCatalogInfo catalogInfo;
1642 OSErr err = ( ref != NULL && vRefNum != NULL ) ? noErr : paramErr;
1643
1644 if( err == noErr ) /* get the volume refNum from the FSRef */
1645 err = FSGetCatalogInfo(ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
1646 if( err == noErr )
1647 *vRefNum = catalogInfo.volume;
1648
1649 mycheck_noerr( err );
1650
1651 return err;
1652}
1653
1654/*****************************************************************************/
1655
1656static OSErr FSGetVolParms( FSVolumeRefNum volRefNum,
1657 UInt32 bufferSize,
1658 GetVolParmsInfoBuffer *volParmsInfo,
1659 UInt32 *actualInfoSize) /* Can Be NULL */
1660{
1661 HParamBlockRec pb;
1662 OSErr err = ( volParmsInfo != NULL ) ? noErr : paramErr;
1663
1664 if( err == noErr )
1665 {
1666 pb.ioParam.ioNamePtr = NULL;
1667 pb.ioParam.ioVRefNum = volRefNum;
1668 pb.ioParam.ioBuffer = (Ptr)volParmsInfo;
1669 pb.ioParam.ioReqCount = (SInt32)bufferSize;
1670 err = PBHGetVolParmsSync(&pb);
1671 }
1672 /* return number of bytes the file system returned in volParmsInfo buffer */
1673 if( err == noErr && actualInfoSize != NULL)
1674 *actualInfoSize = (UInt32)pb.ioParam.ioActCount;
1675
1676 mycheck_noerr( err );
1677
1678 return ( err );
1679}
1680
1681/*****************************************************************************/
1682
1683/* Converts a unicode string to a PString */
1684/* If your code is only for OS X, you can use CFString functions to do all this */
1685/* Since this sample code supports OS 9.1 -> OS X, I have to do this the */
1686/* old fashioned way. */
1687static OSErr UniStrToPStr( const HFSUniStr255 *uniStr,
1688 TextEncoding textEncodingHint,
1689 Boolean isVolumeName,
1690 Str255 pStr )
1691{
1692 UnicodeMapping uMapping;
1693 UnicodeToTextInfo utInfo;
1694 ByteCount unicodeByteLength = 0;
1695 ByteCount unicodeBytesConverted;
1696 ByteCount actualPascalBytes;
1697 OSErr err = (uniStr != NULL && pStr != NULL) ? noErr : paramErr;
1698
1699 /* make sure output is valid in case we get errors or there's nothing to convert */
1700 pStr[0] = 0;
1701
1702 if( err == noErr )
1703 unicodeByteLength = uniStr->length * sizeof(UniChar); /* length can be zero, which is fine */
1704 if( err == noErr && unicodeByteLength != 0 )
1705 {
1706 /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */
1707 if ( kTextEncodingUnknown == textEncodingHint )
1708 {
1709 ScriptCode script;
1710 RegionCode region;
1711
1712 script = (ScriptCode)GetScriptManagerVariable(smSysScript);
1713 region = (RegionCode)GetScriptManagerVariable(smRegionCode);
1714 err = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare,
1715 region, NULL, &textEncodingHint );
1716 if ( err == paramErr )
1717 { /* ok, ignore the region and try again */
1718 err = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare,
1719 kTextRegionDontCare, NULL,
1720 &textEncodingHint );
1721 }
1722 if ( err != noErr ) /* ok... try something */
1723 textEncodingHint = kTextEncodingMacRoman;
1724 }
1725
1726 uMapping.unicodeEncoding = CreateTextEncoding( kTextEncodingUnicodeV2_0,
1727 kUnicodeCanonicalDecompVariant,
1728 kUnicode16BitFormat);
1729 uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint);
1730 uMapping.mappingVersion = kUnicodeUseHFSPlusMapping;
1731
1732 err = CreateUnicodeToTextInfo(&uMapping, &utInfo);
1733 if( err == noErr )
1734 {
1735 err = ConvertFromUnicodeToText( utInfo, unicodeByteLength, uniStr->unicode, kUnicodeLooseMappingsMask,
1736 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */
1737 isVolumeName ? kHFSMaxVolumeNameChars : kHFSPlusMaxFileNameChars,
1738 &unicodeBytesConverted, &actualPascalBytes, &pStr[1]);
1739 }
1740 if( err == noErr )
1741 pStr[0] = actualPascalBytes;
1742
1743 /* verify the result in debug builds -- there's really not anything you can do if it fails */
1744 myverify_noerr(DisposeUnicodeToTextInfo(&utInfo));
1745 }
1746
1747 mycheck_noerr( err );
1748
1749 return ( err );
1750}
1751
1752/*****************************************************************************/
1753
1754 /* Yeah I know there is FSpMakeFSRef, but this way I don't have to */
1755 /* actually have an FSSpec created to make the FSRef, and this is */
1756 /* what FSpMakeFSRef does anyways */
1757static OSErr FSMakeFSRef( FSVolumeRefNum volRefNum,
1758 SInt32 dirID,
1759 ConstStr255Param name,
1760 FSRef *ref )
1761{
1762 FSRefParam pb;
1763 OSErr err = ( ref != NULL ) ? noErr : paramErr;
1764
1765 if( err == noErr )
1766 {
1767 pb.ioVRefNum = volRefNum;
1768 pb.ioDirID = dirID;
1769 pb.ioNamePtr = (StringPtr)name;
1770 pb.newRef = ref;
1771 err = PBMakeFSRefSync(&pb);
1772 }
1773
1774 mycheck_noerr( err );
1775
1776 return ( err );
1777}
1778
1779/*****************************************************************************/
1780
1781 /* This checks the destination to see if an object of the same name as the source */
1782 /* exists or not. If it does we have to special handle the DupeActions */
1783 /* */
1784 /* If kDupeActionReplace we move aside the object by renameing it to ".DeleteMe" */
1785 /* so that it will be invisible (X only), and give a suggestion on what to do with */
1786 /* it if for some unknown reason it survives the copy and the user finds it. This */
1787 /* rename is mainly done to handle the case where the source is in the destination */
1788 /* and the user wants to replace. Basically keeping the source around throughout */
1789 /* the copy, deleting it afterwards. Its also done cause its a good idea not to */
1790 /* dispose of the existing object in case the copy fails */
1791 /* */
1792 /* If kDupeActionRename, we create a unique name for the new object and pass */
1793 /* it back to the caller */
1794static OSErr SetupDestination( const FSRef *destDir,
1795 const DupeAction dupeAction,
1796 HFSUniStr255 *sourceName,
1797 FSRef *deleteMeRef,
1798 Boolean *isReplacing )
1799{
1800 FSRef tmpRef;
1801 OSErr err;
1802
1803 /* check if an object of the same name already exists in the destination */
1804 err = FSMakeFSRefUnicode( destDir, sourceName->length, sourceName->unicode, kTextEncodingUnknown, &tmpRef );
1805 if( err == noErr )
1806 { /* if the user wants to replace the existing */
1807 /* object, rename it to .DeleteMe first. Delete it */
1808 if( dupeAction == kDupeActionReplace ) /* only after copying the new one successfully */
1809 {
1810 err = FSRenameUnicode( &tmpRef, 9, (UniChar*)"\0.\0D\0e\0l\0e\0t\0e\0M\0e", kTextEncodingMacRoman, deleteMeRef );
1811 *isReplacing = ( err == noErr ) ? true : false;
1812 }
1813 else if( dupeAction == kDupeActionRename ) /* if the user wants to just rename it */
1814 err = GetUniqueName( destDir, sourceName ); /* then we get a unique name for the new object */
1815 }
1816 else if ( err == fnfErr ) /* if no object exists then */
1817 err = noErr; /* continue with no error */
1818
1819 return err;
1820}
1821
1822/*****************************************************************************/
1823
1824 /* Given a directory and a name, GetUniqueName will check if an object */
1825 /* with the same name already exists, and if it does it will create */
1826 /* a new, unique name for and return it. */
1827 /* it simply appends a number to the end of the name. It is not */
1828 /* fool proof, and it is limited... I'll take care of that in a */
1829 /* later release */
1830 /* If anyone has any suggestions/better techniques I would love to hear */
1831 /* about them */
1832static OSErr GetUniqueName( const FSRef *destDir,
1833 HFSUniStr255 *sourceName )
1834{
1835 HFSUniStr255 tmpName = *sourceName;
1836 FSRef tmpRef;
1837 unsigned char hexStr[17] = "123456789"; /* yeah, only 9... I'm lazy, sosumi */
1838 long count = 0;
1839 int index;
1840 OSErr err;
1841
1842 /* find the dot, if there is one */
1843 for( index = tmpName.length; index >= 0 && tmpName.unicode[index] != (UniChar) '.'; index-- ) { /* Do Nothing */ }
1844
1845 if( index <= 0) /* no dot or first char is a dot (invisible file), so append to end of name */
1846 index = tmpName.length;
1847 else /* shift the extension up two spots to make room for our digits */
1848 BlockMoveData( tmpName.unicode + index, tmpName.unicode + index + 2, (tmpName.length - index) * 2 );
1849
1850 /* add the space to the name */
1851 tmpName.unicode[ index ] = (UniChar)' ';
1852 /* we're adding two characters to the name */
1853 tmpName.length += 2;
1854
1855 do { /* add the digit to the name */
1856 tmpName.unicode[ index + 1 ] = hexStr[count];
1857 /* check if the file with this new name already exists */
1858 err = FSMakeFSRefUnicode( destDir, tmpName.length, tmpName.unicode, kTextEncodingUnknown, &tmpRef );
1859 count++;
1860 } while( err == noErr && count < 10 );
1861
1862 if( err == fnfErr )
1863 {
1864 err = noErr;
1865 *sourceName = tmpName;
1866 }
1867
1868 return err;
1869}
1870
1871/*****************************************************************************/
1872
1873static OSErr GetObjectName( const FSRef *sourceRef,
1874 HFSUniStr255 *sourceName, /* can be NULL */
1875 TextEncoding *sourceEncoding ) /* can be NULL */
1876{
1877 FSCatalogInfo catInfo;
1878 FSCatalogInfoBitmap whichInfo = (sourceEncoding != NULL) ? kFSCatInfoTextEncoding : kFSCatInfoNone;
1879 OSErr err;
1880
1881 err = FSGetCatalogInfo( sourceRef, whichInfo, &catInfo, sourceName, NULL, NULL );
1882 if( err == noErr && sourceEncoding != NULL )
1883 *sourceEncoding = catInfo.textEncodingHint;
1884
1885 return err;
1886}
1887
1888/*****************************************************************************/
1889
1890static OSErr CreateFolder( const FSRef *sourceRef,
1891 const FSRef *destDirRef,
1892 const FSCatalogInfo *catalogInfo,
1893 const HFSUniStr255 *folderName,
1894 CopyParams *params,
1895 FSRef *newFSRefPtr,
1896 FSSpec *newFSSpecPtr )
1897{
1898 FSCatalogInfo tmpCatInfo;
1899 FSPermissionInfo origPermissions;
1900 OSErr err = ( sourceRef != NULL && destDirRef != NULL && catalogInfo != NULL &&
1901 folderName != NULL && newFSRefPtr != NULL ) ? noErr : paramErr;
1902
1903 if( err == noErr )
1904 { /* store away the catInfo, create date and permissions on the orig folder */
1905 tmpCatInfo = *catalogInfo;
1906 origPermissions = *((FSPermissionInfo*)catalogInfo->permissions);
1907 }
1908 if( err == noErr ) /* create the new folder */
1909 err = DoCreateFolder( sourceRef, destDirRef, &tmpCatInfo, folderName, params, newFSRefPtr, newFSSpecPtr );
1910 if( err == noErr && !params->copyingToDropFolder )
1911 { /* if its not a drop box, set the permissions on the new folder */
1912 *((FSPermissionInfo*)tmpCatInfo.permissions) = origPermissions;
1913 err = FSSetCatalogInfo( newFSRefPtr, kFSCatInfoPermissions, &tmpCatInfo );
1914 }
1915
1916 mycheck_noerr( err );
1917
1918 return err;
1919}
1920
1921/*****************************************************************************/
1922
1923static OSErr DoCreateFolder(const FSRef *sourceRef,
1924 const FSRef *destDirRef,
1925 const FSCatalogInfo *catalogInfo,
1926 const HFSUniStr255 *folderName,
1927 CopyParams *params,
1928 FSRef *newFSRefPtr,
1929 FSSpec *newFSSpecPtr)
1930{
1931 FSCatalogInfo catInfo = *catalogInfo;
1932 OSErr err;
1933
1934 /* Clear the "inited" bit so that the Finder positions the icon for us. */
1935 ((FInfo *)(catInfo.finderInfo))->fdFlags &= ~kHasBeenInited;
1936
1937 /* we need to have user level read/write/execute access to the folder we are going to create, */
1938 /* otherwise FSCreateDirectoryUnicode will return -5000 (afpAccessDenied), */
1939 /* and the FSRef returned will be invalid, yet the folder is created... bug? */
1940 ((FSPermissionInfo*) catInfo.permissions)->mode |= kRWXUserAccessMask;
1941
1942 err = FSCreateDirectoryUnicode( destDirRef, folderName->length,
1943 folderName->unicode, kFSCatInfoSettableInfo,
1944 &catInfo, newFSRefPtr,
1945 newFSSpecPtr, NULL);
1946
1947 /* With the new APIs, folders can have forks as well as files. Before */
1948 /* we start copying items in the folder, we must copy over the forks */
1949 /* Currently, MacOS doesn't support any file systems that have forks in */
1950 /* folders, but the API supports it so (for possible future */
1951 /* compatability) I kept this in here. */
1952 if( err == noErr )
1953 err = CopyForks( sourceRef, newFSRefPtr, params );
1954
1955 mycheck_noerr( err );
1956
1957 return err;
1958}
1959
1960/*****************************************************************************/
1961
1962 /* This is the DisposeDataProc that is used by the GenLinkedList in FSCopyFolder */
1963 /* Simply disposes of the data we created and returns */
1964static pascal void MyDisposeDataProc( void *pData )
1965{
1966 if( pData != NULL )
1967 DisposePtr( (char*) pData );
1968}
1969
1970/*****************************************************************************/
1971
1972 /* This is the DisposeDataProc that is used by the GenLinkedList in CopyItemForks */
1973 /* Simply closes the resource fork (if opened, != 0) and disposes of the memory */
1974static pascal void MyCloseForkProc( void *pData )
1975{
1976 SInt16 refNum;
1977
1978 if( pData == NULL )
1979 return;
1980
1981 refNum = ((ForkTrackerPtr)pData)->forkDestRefNum;
1982 if( refNum != 0 )
1983 myverify_noerr( FSCloseFork( refNum ) ); /* the fork was opened, so close it */
1984
1985 DisposePtr( (char*) pData );
1986} \ No newline at end of file