diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/mac_updater/FSCopyObject.c | |
parent | README.txt (diff) | |
download | meta-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.c | 1986 |
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 | |||
60 | enum { /* 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 | |||
67 | enum { /* CheckForDestInsideSrc */ | ||
68 | errFSDestInsideSource = -1234 | ||
69 | }; | ||
70 | |||
71 | enum { | ||
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 */ | ||
108 | struct CopyParams { | ||
109 | void *copyBuffer; | ||
110 | ByteCount copyBufferSize; | ||
111 | Boolean copyingToDropFolder; | ||
112 | Boolean copyingToLocalVolume; | ||
113 | Boolean volHasCopyFile; | ||
114 | DupeAction dupeAction; | ||
115 | }; | ||
116 | typedef 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 */ | ||
121 | struct FilterParams { | ||
122 | FSCatalogInfoBitmap whichInfo; | ||
123 | CopyObjectFilterProcPtr filterProcPtr; | ||
124 | Boolean containerChanged; | ||
125 | Boolean wantSpec; | ||
126 | Boolean wantName; | ||
127 | void *yourDataPtr; | ||
128 | }; | ||
129 | typedef 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... */ | ||
137 | struct ForkTracker { | ||
138 | HFSUniStr255 forkName; | ||
139 | SInt64 forkSize; | ||
140 | SInt16 forkDestRefNum; | ||
141 | }; | ||
142 | typedef struct ForkTracker ForkTracker; | ||
143 | typedef 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. */ | ||
148 | struct FolderListData | ||
149 | { | ||
150 | FSRef sourceDirRef; | ||
151 | FSRef destDirRef; | ||
152 | UInt32 level; | ||
153 | }; | ||
154 | typedef struct FolderListData FolderListData; | ||
155 | |||
156 | /* The FSCopyFolderGlobals data structure holds the information needed to */ | ||
157 | /* copy a directory */ | ||
158 | struct 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 | }; | ||
177 | typedef struct FSCopyFolderGlobals FSCopyFolderGlobals; | ||
178 | |||
179 | /* The FSDeleteObjectGlobals data structure holds information needed to */ | ||
180 | /* recursively delete a directory */ | ||
181 | struct FSDeleteObjectGlobals | ||
182 | { | ||
183 | FSCatalogInfo catalogInfo; /* FSCatalogInfo */ | ||
184 | ItemCount actualObjects; /* number of objects returned */ | ||
185 | OSErr result; /* result */ | ||
186 | }; | ||
187 | typedef struct FSDeleteObjectGlobals FSDeleteObjectGlobals; | ||
188 | |||
189 | #pragma mark ----- Local Prototypes ----- | ||
190 | |||
191 | static 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 | |||
201 | static 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 | |||
210 | static 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 | |||
218 | static 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 | |||
225 | static 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 | |||
233 | static 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 | |||
243 | static OSErr ProcessFolderList ( FSCopyFolderGlobals *folderGlobals ); | ||
244 | |||
245 | static OSErr CopyFolder ( FSCopyFolderGlobals *folderGlobals ); | ||
246 | |||
247 | static OSErr CheckForDestInsideSrc ( const FSRef *source, | ||
248 | const FSRef *destDir ); | ||
249 | |||
250 | static OSErr CopyForks ( const FSRef *source, | ||
251 | const FSRef *dest, | ||
252 | CopyParams *params ); | ||
253 | |||
254 | static OSErr CopyForksToDisk ( const FSRef *source, | ||
255 | const FSRef *dest, | ||
256 | CopyParams *params ); | ||
257 | |||
258 | static OSErr CopyForksToDropBox ( const FSRef *source, | ||
259 | const FSRef *dest, | ||
260 | CopyParams *params ); | ||
261 | |||
262 | static OSErr OpenAllForks ( const FSRef *dest, | ||
263 | GenLinkedList *forkList ); | ||
264 | |||
265 | static OSErr WriteFork ( const SInt16 srcRefNum, | ||
266 | const SInt16 destRefNum, | ||
267 | const CopyParams *params, | ||
268 | const SInt64 forkSize ); | ||
269 | |||
270 | static UInt32 CalcBufferSizeForVol ( const GetVolParmsInfoBuffer *volParms, | ||
271 | UInt32 volParmsSize ); | ||
272 | |||
273 | static UInt32 BufferSizeForVolSpeed ( UInt32 volumeBytesPerSecond ); | ||
274 | |||
275 | static OSErr FSDeleteFolder ( const FSRef *container ); | ||
276 | |||
277 | static void FSDeleteFolderLevel ( const FSRef *container, | ||
278 | FSDeleteObjectGlobals *theGlobals ); | ||
279 | |||
280 | static OSErr IsDropBox ( const FSRef *source, | ||
281 | Boolean *isDropBox ); | ||
282 | |||
283 | static OSErr GetMagicBusyCreateDate( UTCDateTime *date ); | ||
284 | |||
285 | static OSErr FSGetVRefNum ( const FSRef *ref, | ||
286 | FSVolumeRefNum *vRefNum ); | ||
287 | |||
288 | static OSErr FSGetVolParms ( FSVolumeRefNum volRefNum, | ||
289 | UInt32 bufferSize, | ||
290 | GetVolParmsInfoBuffer*volParmsInfo, | ||
291 | UInt32 *actualInfoSize ); /* Can Be NULL */ | ||
292 | |||
293 | static OSErr UniStrToPStr ( const HFSUniStr255 *uniStr, | ||
294 | TextEncoding textEncodingHint, | ||
295 | Boolean isVolumeName, | ||
296 | Str255 pStr ); | ||
297 | |||
298 | static OSErr FSMakeFSRef ( FSVolumeRefNum volRefNum, | ||
299 | SInt32 dirID, | ||
300 | ConstStr255Param name, | ||
301 | FSRef *ref ); | ||
302 | |||
303 | static OSErr SetupDestination ( const FSRef *destDir, | ||
304 | const DupeAction dupeAction, | ||
305 | HFSUniStr255 *sourceName, | ||
306 | FSRef *deleteMeRef, | ||
307 | Boolean *isReplacing); | ||
308 | |||
309 | static OSErr GetUniqueName ( const FSRef *destDir, | ||
310 | HFSUniStr255 *sourceName ); | ||
311 | |||
312 | static OSErr GetObjectName ( const FSRef *sourceRef, | ||
313 | HFSUniStr255 *sourceName, | ||
314 | TextEncoding *sourceEncoding ); | ||
315 | |||
316 | static 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 | |||
324 | static 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 | |||
332 | static pascal void MyDisposeDataProc ( void *pData ); | ||
333 | |||
334 | static 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. */ | ||
343 | OSErr 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, ©Params, &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, ©Params, &filterParams, maxLevels, &tmpObjectRef, newObjectSpec ); | ||
407 | } | ||
408 | else /* no */ | ||
409 | err = FSCopyFile(source, destDir, &sourceCatInfo, &tmpObjectName, ©Params, &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 */ | ||
455 | static 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, ©Params->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 | |||
547 | static 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 | |||
597 | static 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 */ | ||
626 | static 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 | /* */ | ||
681 | static 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 | |||
790 | static 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. */ | ||
902 | static 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 */ | ||
945 | static 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 | /* */ | ||
1063 | static 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. */ | ||
1111 | static 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 */ | ||
1126 | static 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. */ | ||
1190 | static 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 */ | ||
1238 | static 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 */ | ||
1307 | static 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. */ | ||
1382 | static 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. */ | ||
1418 | static 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 | |||
1446 | OSErr 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 | |||
1484 | static 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 | |||
1501 | static 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 */ | ||
1560 | static 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!). */ | ||
1621 | static 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 | |||
1638 | static 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 | |||
1656 | static 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. */ | ||
1687 | static 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 */ | ||
1757 | static 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 */ | ||
1794 | static 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 */ | ||
1832 | static 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 | |||
1873 | static 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 | |||
1890 | static 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 | |||
1923 | static 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 */ | ||
1964 | static 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 */ | ||
1974 | static 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 | ||