1 | /*
|
---|
2 | File: FSCopyObject.c
|
---|
3 |
|
---|
4 | Contains: A Copy/Delete Files/Folders engine which uses the HFS+ API's.
|
---|
5 | This code is a combination of MoreFilesX and MPFileCopy
|
---|
6 | with some added features. This code will run on OS 9.1 and up
|
---|
7 | and 10.1.x (Classic and Carbon)
|
---|
8 |
|
---|
9 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
|
---|
10 | ("Apple") in consideration of your agreement to the following terms, and your
|
---|
11 | use, installation, modification or redistribution of this Apple software
|
---|
12 | constitutes acceptance of these terms. If you do not agree with these terms,
|
---|
13 | please do not use, install, modify or redistribute this Apple software.
|
---|
14 |
|
---|
15 | In consideration of your agreement to abide by the following terms, and subject
|
---|
16 | to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
|
---|
17 | copyrights in this original Apple software (the "Apple Software"), to use,
|
---|
18 | reproduce, modify and redistribute the Apple Software, with or without
|
---|
19 | modifications, in source and/or binary forms; provided that if you redistribute
|
---|
20 | the Apple Software in its entirety and without modifications, you must retain
|
---|
21 | this notice and the following text and disclaimers in all such redistributions of
|
---|
22 | the Apple Software. Neither the name, trademarks, service marks or logos of
|
---|
23 | Apple Computer, Inc. may be used to endorse or promote products derived from the
|
---|
24 | Apple Software without specific prior written permission from Apple. Except as
|
---|
25 | expressly stated in this notice, no other rights or licenses, express or implied,
|
---|
26 | are granted by Apple herein, including but not limited to any patent rights that
|
---|
27 | may be infringed by your derivative works or by other works in which the Apple
|
---|
28 | Software may be incorporated.
|
---|
29 |
|
---|
30 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
|
---|
31 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
|
---|
32 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
---|
33 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
|
---|
34 | COMBINATION WITH YOUR PRODUCTS.
|
---|
35 |
|
---|
36 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
|
---|
37 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
---|
38 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
---|
39 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
|
---|
40 | OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
|
---|
41 | (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
|
---|
42 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
---|
43 |
|
---|
44 | Copyright © 2002 Apple Computer, Inc., All Rights Reserved
|
---|
45 | */
|
---|
46 |
|
---|
47 | #include "FSCopyObject.h"
|
---|
48 | #include <UnicodeConverter.h>
|
---|
49 | #include <stddef.h>
|
---|
50 | #include <string.h>
|
---|
51 |
|
---|
52 | /*
|
---|
53 |
|
---|
54 | */
|
---|
55 |
|
---|
56 | #pragma mark ----- Tunable Parameters -----
|
---|
57 |
|
---|
58 | // The following constants control the behavior of the copy engine.
|
---|
59 |
|
---|
60 | enum { // BufferSizeForThisVolumeSpeed
|
---|
61 | // kDefaultCopyBufferSize = 2L * 1024 * 1024, // Fast be not very responsive.
|
---|
62 | kDefaultCopyBufferSize = 256L * 1024, // Slower, but can still use machine.
|
---|
63 | kMaximumCopyBufferSize = 2L * 1024 * 1024,
|
---|
64 | kMinimumCopyBufferSize = 1024
|
---|
65 | };
|
---|
66 |
|
---|
67 | enum { // CalculateForksToCopy
|
---|
68 | kExpectedForkCount = 10 // Number of non-classic forks we expect.
|
---|
69 | }; // (i.e. non resource/data forks)
|
---|
70 |
|
---|
71 | enum { // CheckForDestInsideSource
|
---|
72 | errFSDestInsideSource = -1234
|
---|
73 | };
|
---|
74 |
|
---|
75 | enum {
|
---|
76 | // for use with PBHGetDirAccess in IsDropBox
|
---|
77 | kPrivilegesMask = kioACAccessUserWriteMask | kioACAccessUserReadMask | kioACAccessUserSearchMask,
|
---|
78 |
|
---|
79 | // for use with FSGetCatalogInfo and FSPermissionInfo->mode
|
---|
80 | // from sys/stat.h... note -- sys/stat.h definitions are in octal
|
---|
81 | //
|
---|
82 | // You can use these values to adjust the users/groups permissions
|
---|
83 | // on a file/folder with FSSetCatalogInfo and extracting the
|
---|
84 | // kFSCatInfoPermissions field. See code below for examples
|
---|
85 | kRWXUserAccessMask = 0x01C0,
|
---|
86 | kReadAccessUser = 0x0100,
|
---|
87 | kWriteAccessUser = 0x0080,
|
---|
88 | kExecuteAccessUser = 0x0040,
|
---|
89 |
|
---|
90 | kRWXGroupAccessMask = 0x0038,
|
---|
91 | kReadAccessGroup = 0x0020,
|
---|
92 | kWriteAccessGroup = 0x0010,
|
---|
93 | kExecuteAccessGroup = 0x0008,
|
---|
94 |
|
---|
95 | kRWXOtherAccessMask = 0x0007,
|
---|
96 | kReadAccessOther = 0x0004,
|
---|
97 | kWriteAccessOther = 0x0002,
|
---|
98 | kExecuteAccessOther = 0x0001,
|
---|
99 |
|
---|
100 | kDropFolderValue = kWriteAccessOther | kExecuteAccessOther
|
---|
101 | };
|
---|
102 |
|
---|
103 | #pragma mark ----- Struct Definitions -----
|
---|
104 |
|
---|
105 | #define VolHasCopyFile(volParms) \
|
---|
106 | (((volParms)->vMAttrib & (1L << bHasCopyFile)) != 0)
|
---|
107 |
|
---|
108 | // The CopyParams data structure holds the copy buffer used
|
---|
109 | // when copying the forks over, as well as special case
|
---|
110 | // info on the destination
|
---|
111 | struct CopyParams {
|
---|
112 | UTCDateTime magicBusyCreateDate;
|
---|
113 | void *copyBuffer;
|
---|
114 | ByteCount copyBufferSize;
|
---|
115 | Boolean copyingToDropFolder;
|
---|
116 | Boolean copyingToLocalVolume;
|
---|
117 | };
|
---|
118 | typedef struct CopyParams CopyParams;
|
---|
119 |
|
---|
120 | // The FilterParams data structure holds the date and info
|
---|
121 | // that the caller wants passed into the Filter Proc, as well
|
---|
122 | // as the Filter Proc Pointer itself
|
---|
123 | struct FilterParams {
|
---|
124 | FSCatalogInfoBitmap whichInfo;
|
---|
125 | CopyObjectFilterProcPtr filterProcPtr;
|
---|
126 | FSSpec fileSpec;
|
---|
127 | FSSpec *fileSpecPtr;
|
---|
128 | HFSUniStr255 fileName;
|
---|
129 | HFSUniStr255 *fileNamePtr;
|
---|
130 | void *yourDataPtr;
|
---|
131 | };
|
---|
132 | typedef struct FilterParams FilterParams;
|
---|
133 |
|
---|
134 | // The ForkTracker data structure holds information about a specific fork,
|
---|
135 | // specifically the name and the refnum. We use this to build a list of
|
---|
136 | // all the forks before we start copying them. We need to do this because,
|
---|
137 | // if we're copying into a drop folder, we must open all the forks before
|
---|
138 | // we start copying data into any of them.
|
---|
139 | // Plus it's a convenient way to keep track of all the forks...
|
---|
140 | struct ForkTracker {
|
---|
141 | HFSUniStr255 forkName;
|
---|
142 | SInt64 forkSize;
|
---|
143 | SInt16 forkDestRefNum;
|
---|
144 | };
|
---|
145 | typedef struct ForkTracker ForkTracker;
|
---|
146 | typedef ForkTracker *ForkTrackerPtr;
|
---|
147 |
|
---|
148 | // The FSCopyObjectGlobals data structure holds information needed to do
|
---|
149 | // the recursive copy of a directory.
|
---|
150 | struct FSCopyObjectGlobals
|
---|
151 | {
|
---|
152 | FSCatalogInfo catalogInfo;
|
---|
153 | FSRef ref; /* FSRef to the source file/folder*/
|
---|
154 | FSRef destRef; /* FSRef to the destination directory */
|
---|
155 | CopyParams *copyParams; /* pointer to info needed to do the copy */
|
---|
156 | FilterParams *filterParams; /* pointer to info needed for the optional filter proc */
|
---|
157 | ItemCount maxLevels; /* maximum levels to iterate through */
|
---|
158 | ItemCount currentLevel; /* the current level FSCopyFolderLevel is on */
|
---|
159 | Boolean quitFlag; /* set to true if filter wants to kill interation */
|
---|
160 | Boolean containerChanged; /* temporary - set to true if the current container changed during iteration */
|
---|
161 | OSErr result; /* result */
|
---|
162 | ItemCount actualObjects; /* number of objects returned */
|
---|
163 | };
|
---|
164 | typedef struct FSCopyObjectGlobals FSCopyObjectGlobals;
|
---|
165 |
|
---|
166 | // The FSDeleteObjectGlobals data structure holds information needed to
|
---|
167 | // recursively delete a directory
|
---|
168 | struct FSDeleteObjectGlobals
|
---|
169 | {
|
---|
170 | FSCatalogInfo catalogInfo; /* FSCatalogInfo */
|
---|
171 | ItemCount actualObjects; /* number of objects returned */
|
---|
172 | OSErr result; /* result */
|
---|
173 | };
|
---|
174 | typedef struct FSDeleteObjectGlobals FSDeleteObjectGlobals;
|
---|
175 |
|
---|
176 | #pragma mark ----- Local Prototypes -----
|
---|
177 |
|
---|
178 | static OSErr FSCopyFile( const FSRef *source,
|
---|
179 | const FSRef *destDir,
|
---|
180 | const HFSUniStr255 *destName, /* can be NULL (no rename during copy) */
|
---|
181 | CopyParams *copyParams,
|
---|
182 | FilterParams *filterParams,
|
---|
183 | FSRef *newFile); /* can be NULL */
|
---|
184 |
|
---|
185 | static OSErr CopyFile( const FSRef *source,
|
---|
186 | FSCatalogInfo *sourceCatInfo,
|
---|
187 | const FSRef *destDir,
|
---|
188 | const HFSUniStr255 *destName,
|
---|
189 | CopyParams *copyParams,
|
---|
190 | FSRef *newRef); /* can be NULL */
|
---|
191 |
|
---|
192 | static OSErr FSUsePBHCopyFile( const FSRef *srcFileRef,
|
---|
193 | const FSRef *dstDirectoryRef,
|
---|
194 | UniCharCount nameLength,
|
---|
195 | const UniChar *copyName, /* can be NULL (no rename during copy) */
|
---|
196 | TextEncoding textEncodingHint,
|
---|
197 | FSRef *newRef); /* can be NULL */
|
---|
198 |
|
---|
199 | static OSErr DoCopyFile( const FSRef *source,
|
---|
200 | FSCatalogInfo *sourceCatInfo,
|
---|
201 | const FSRef *destDir,
|
---|
202 | const HFSUniStr255 *destName,
|
---|
203 | CopyParams *params,
|
---|
204 | FSRef *newRef); /* can be NULL */
|
---|
205 |
|
---|
206 | static OSErr FSCopyFolder( const FSRef *source,
|
---|
207 | const FSRef *destDir,
|
---|
208 | const HFSUniStr255 *destName, /* can be NULL (no rename during copy) */
|
---|
209 | CopyParams* copyParams,
|
---|
210 | FilterParams *filterParams,
|
---|
211 | ItemCount maxLevels,
|
---|
212 | FSRef* newDir); /* can be NULL */
|
---|
213 |
|
---|
214 | static OSErr FSCopyFolderLevel( FSCopyObjectGlobals *theGlobals, const HFSUniStr255 *destName );
|
---|
215 |
|
---|
216 | static OSErr CheckForDestInsideSource( const FSRef *source,
|
---|
217 | const FSRef *destDir);
|
---|
218 |
|
---|
219 | static OSErr CopyItemsForks( const FSRef *source,
|
---|
220 | const FSRef *dest,
|
---|
221 | CopyParams *params);
|
---|
222 |
|
---|
223 | static OSErr OpenAllForks( const FSRef *dest,
|
---|
224 | const ForkTrackerPtr dataFork,
|
---|
225 | const ForkTrackerPtr rsrcFork,
|
---|
226 | ForkTrackerPtr otherForks,
|
---|
227 | ItemCount otherForksCount);
|
---|
228 |
|
---|
229 | static OSErr CopyFork( const FSRef *source,
|
---|
230 | const FSRef *dest,
|
---|
231 | const ForkTrackerPtr sourceFork,
|
---|
232 | const CopyParams *params);
|
---|
233 |
|
---|
234 | static OSErr CloseAllForks( SInt16 dataRefNum,
|
---|
235 | SInt16 rsrcRefNum,
|
---|
236 | ForkTrackerPtr otherForks,
|
---|
237 | ItemCount otherForksCount);
|
---|
238 |
|
---|
239 | static OSErr CalculateForksToCopy( const FSRef *source,
|
---|
240 | const ForkTrackerPtr dataFork,
|
---|
241 | const ForkTrackerPtr rsrcFork,
|
---|
242 | ForkTrackerPtr *otherForksParam,
|
---|
243 | ItemCount *otherForksCountParam);
|
---|
244 |
|
---|
245 | static OSErr CalculateBufferSize( const FSRef *source,
|
---|
246 | const FSRef *destDir,
|
---|
247 | ByteCount * bufferSize);
|
---|
248 |
|
---|
249 | static ByteCount BufferSizeForThisVolume(FSVolumeRefNum vRefNum);
|
---|
250 |
|
---|
251 | static ByteCount BufferSizeForThisVolumeSpeed(UInt32 volumeBytesPerSecond);
|
---|
252 |
|
---|
253 | static OSErr IsDropBox( const FSRef* source,
|
---|
254 | Boolean *isDropBox);
|
---|
255 |
|
---|
256 | static OSErr GetMagicBusyCreationDate( UTCDateTime *date );
|
---|
257 |
|
---|
258 | static Boolean CompareHFSUniStr255(const HFSUniStr255 *lhs,
|
---|
259 | const HFSUniStr255 *rhs);
|
---|
260 |
|
---|
261 | static OSErr FSGetVRefNum( const FSRef *ref,
|
---|
262 | FSVolumeRefNum *vRefNum);
|
---|
263 |
|
---|
264 | static OSErr FSGetVolParms( FSVolumeRefNum volRefNum,
|
---|
265 | UInt32 bufferSize,
|
---|
266 | GetVolParmsInfoBuffer *volParmsInfo,
|
---|
267 | UInt32 *actualInfoSize); /* Can Be NULL */
|
---|
268 |
|
---|
269 | static OSErr UnicodeNameGetHFSName( UniCharCount nameLength,
|
---|
270 | const UniChar *name,
|
---|
271 | TextEncoding textEncodingHint,
|
---|
272 | Boolean isVolumeName,
|
---|
273 | Str31 hfsName);
|
---|
274 |
|
---|
275 | static OSErr FSMakeFSRef( FSVolumeRefNum volRefNum,
|
---|
276 | SInt32 dirID,
|
---|
277 | ConstStr255Param name,
|
---|
278 | FSRef *ref);
|
---|
279 |
|
---|
280 | static OSErr FSDeleteFolder( const FSRef *container );
|
---|
281 |
|
---|
282 | static void FSDeleteFolderLevel( const FSRef *container,
|
---|
283 | FSDeleteObjectGlobals *theGlobals);
|
---|
284 |
|
---|
285 | /*****************************************************************************/
|
---|
286 | /*****************************************************************************/
|
---|
287 | /*****************************************************************************/
|
---|
288 |
|
---|
289 | #pragma mark ----- Copy Objects -----
|
---|
290 |
|
---|
291 | // This routine acts as the top level of the copy engine. It exists
|
---|
292 | // to a) present a nicer API than the various recursive routines, and
|
---|
293 | // b) minimise the local variables in the recursive routines.
|
---|
294 | OSErr FSCopyObject( const FSRef *source,
|
---|
295 | const FSRef *destDir,
|
---|
296 | UniCharCount nameLength,
|
---|
297 | const UniChar *copyName, // can be NULL (no rename during copy)
|
---|
298 | ItemCount maxLevels,
|
---|
299 | FSCatalogInfoBitmap whichInfo,
|
---|
300 | Boolean wantFSSpec,
|
---|
301 | Boolean wantName,
|
---|
302 | CopyObjectFilterProcPtr filterProcPtr, // can be NULL
|
---|
303 | void *yourDataPtr, // can be NULL
|
---|
304 | FSRef *newObject) // can be NULL
|
---|
305 | {
|
---|
306 | CopyParams copyParams;
|
---|
307 | FilterParams filterParams;
|
---|
308 | HFSUniStr255 destName;
|
---|
309 | HFSUniStr255 *destNamePtr;
|
---|
310 | Boolean isDirectory;
|
---|
311 | OSErr osErr = ( source != NULL && destDir != NULL ) ? noErr : paramErr;
|
---|
312 |
|
---|
313 | if (copyName)
|
---|
314 | {
|
---|
315 | if (nameLength <= 255)
|
---|
316 | {
|
---|
317 | BlockMoveData(copyName, destName.unicode, nameLength * sizeof(UniChar));
|
---|
318 | destName.length = nameLength;
|
---|
319 | destNamePtr = &destName;
|
---|
320 | }
|
---|
321 | else
|
---|
322 | osErr = paramErr;
|
---|
323 | }
|
---|
324 | else
|
---|
325 | destNamePtr = NULL;
|
---|
326 |
|
---|
327 | // we want the settable info no matter what the user asked for
|
---|
328 | filterParams.whichInfo = whichInfo | kFSCatInfoSettableInfo;
|
---|
329 | filterParams.filterProcPtr = filterProcPtr;
|
---|
330 | filterParams.fileSpecPtr = ( wantFSSpec ) ? &filterParams.fileSpec : NULL;
|
---|
331 | filterParams.fileNamePtr = ( wantName ) ? &filterParams.fileName : NULL;
|
---|
332 | filterParams.yourDataPtr = yourDataPtr;
|
---|
333 |
|
---|
334 | // Calculate the optimal buffer size to copy the forks over
|
---|
335 | // and create the buffer
|
---|
336 | if( osErr == noErr )
|
---|
337 | osErr = CalculateBufferSize( source, destDir, ©Params.copyBufferSize);
|
---|
338 |
|
---|
339 | if( osErr == noErr )
|
---|
340 | {
|
---|
341 | copyParams.copyBuffer = NewPtr( copyParams.copyBufferSize );
|
---|
342 | if( copyParams.copyBuffer == NULL )
|
---|
343 | osErr = memFullErr;
|
---|
344 | }
|
---|
345 |
|
---|
346 | if( osErr == noErr )
|
---|
347 | osErr = GetMagicBusyCreationDate( ©Params.magicBusyCreateDate );
|
---|
348 |
|
---|
349 | if( osErr == noErr ) // figure out if source is a file or folder
|
---|
350 | { // if it is on a local volume,
|
---|
351 | // if destination is a drop box
|
---|
352 | GetVolParmsInfoBuffer volParms;
|
---|
353 | FSCatalogInfo tmpCatInfo;
|
---|
354 | FSVolumeRefNum destVRefNum;
|
---|
355 |
|
---|
356 | // to figure out if the souce is a folder or directory
|
---|
357 | osErr = FSGetCatalogInfo(source, kFSCatInfoNodeFlags, &tmpCatInfo, NULL, NULL, NULL);
|
---|
358 | if( osErr == noErr )
|
---|
359 | {
|
---|
360 | isDirectory = ((tmpCatInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0);
|
---|
361 | // are we copying to a drop folder?
|
---|
362 | osErr = IsDropBox( destDir, ©Params.copyingToDropFolder );
|
---|
363 | }
|
---|
364 | if( osErr == noErr )
|
---|
365 | osErr = FSGetVRefNum(destDir, &destVRefNum);
|
---|
366 | if( osErr == noErr )
|
---|
367 | osErr = FSGetVolParms( destVRefNum, sizeof(volParms), &volParms, NULL );
|
---|
368 | if( osErr == noErr ) // volParms.vMServerAdr is non-zero for remote volumes
|
---|
369 | copyParams.copyingToLocalVolume = (volParms.vMServerAdr == 0);
|
---|
370 | }
|
---|
371 |
|
---|
372 | // now copy the file/folder...
|
---|
373 | if( osErr == noErr )
|
---|
374 | { // is it a folder?
|
---|
375 | if ( isDirectory )
|
---|
376 | { // yes
|
---|
377 | osErr = CheckForDestInsideSource(source, destDir);
|
---|
378 | if( osErr == noErr )
|
---|
379 | osErr = FSCopyFolder( source, destDir, destNamePtr, ©Params, &filterParams, maxLevels, newObject );
|
---|
380 | }
|
---|
381 | else // no
|
---|
382 | osErr = FSCopyFile(source, destDir, destNamePtr, ©Params, &filterParams, newObject);
|
---|
383 | }
|
---|
384 |
|
---|
385 | // Clean up for space and safety... Who me?
|
---|
386 | if( copyParams.copyBuffer != NULL )
|
---|
387 | DisposePtr((char*)copyParams.copyBuffer);
|
---|
388 |
|
---|
389 | mycheck_noerr( osErr ); // put up debug assert in debug builds
|
---|
390 |
|
---|
391 | return osErr;
|
---|
392 | }
|
---|
393 |
|
---|
394 | /*****************************************************************************/
|
---|
395 |
|
---|
396 | #pragma mark ----- Copy Files -----
|
---|
397 |
|
---|
398 | OSErr FSCopyFile( const FSRef *source,
|
---|
399 | const FSRef *destDir,
|
---|
400 | const HFSUniStr255 *destName,
|
---|
401 | CopyParams *copyParams,
|
---|
402 | FilterParams *filterParams,
|
---|
403 | FSRef *newFile)
|
---|
404 | {
|
---|
405 | FSCatalogInfo sourceCatInfo;
|
---|
406 | FSRef tmpRef;
|
---|
407 | OSErr osErr = ( source != NULL && destDir != NULL &&
|
---|
408 | copyParams != NULL && filterParams != NULL ) ? noErr : paramErr;
|
---|
409 |
|
---|
410 | // get needed info about the source file
|
---|
411 | if ( osErr == noErr )
|
---|
412 | {
|
---|
413 | if (destName)
|
---|
414 | {
|
---|
415 | osErr = FSGetCatalogInfo(source, filterParams->whichInfo, &sourceCatInfo, NULL, NULL, NULL);
|
---|
416 | filterParams->fileName = *destName;
|
---|
417 | }
|
---|
418 | else
|
---|
419 | osErr = FSGetCatalogInfo(source, filterParams->whichInfo, &sourceCatInfo, &filterParams->fileName, NULL, NULL);
|
---|
420 | }
|
---|
421 | if( osErr == noErr )
|
---|
422 | osErr = CopyFile(source, &sourceCatInfo, destDir, &filterParams->fileName, copyParams, &tmpRef);
|
---|
423 |
|
---|
424 | // Call the IterateFilterProc _after_ the new file was created
|
---|
425 | // even if an error occured
|
---|
426 | if( filterParams->filterProcPtr != NULL )
|
---|
427 | {
|
---|
428 | (void) CallCopyObjectFilterProc(filterParams->filterProcPtr, false, 0, osErr, &sourceCatInfo,
|
---|
429 | &tmpRef, filterParams->fileSpecPtr,
|
---|
430 | filterParams->fileNamePtr, filterParams->yourDataPtr);
|
---|
431 | }
|
---|
432 |
|
---|
433 | if( osErr == noErr && newFile != NULL )
|
---|
434 | *newFile = tmpRef;
|
---|
435 |
|
---|
436 | mycheck_noerr(osErr); // put up debug assert in debug builds
|
---|
437 |
|
---|
438 | return osErr;
|
---|
439 | }
|
---|
440 |
|
---|
441 | /*****************************************************************************/
|
---|
442 |
|
---|
443 | OSErr CopyFile( const FSRef *source,
|
---|
444 | FSCatalogInfo *sourceCatInfo,
|
---|
445 | const FSRef *destDir,
|
---|
446 | ConstHFSUniStr255Param destName,
|
---|
447 | CopyParams *params,
|
---|
448 | FSRef* newFile)
|
---|
449 | {
|
---|
450 | OSErr osErr = paramErr;
|
---|
451 |
|
---|
452 | // Clear the "inited" bit so that the Finder positions the icon for us.
|
---|
453 | ((FInfo *)(sourceCatInfo->finderInfo))->fdFlags &= ~kHasBeenInited;
|
---|
454 |
|
---|
455 | // if the destination is on a remote volume, try to use PBHCopyFile
|
---|
456 | if( params->copyingToLocalVolume == 0 )
|
---|
457 | osErr = FSUsePBHCopyFile( source, destDir, 0, NULL, kTextEncodingUnknown, newFile );
|
---|
458 |
|
---|
459 | // if PBHCopyFile didn't work or not supported,
|
---|
460 | if( osErr != noErr ) // then try old school file transfer
|
---|
461 | osErr = DoCopyFile( source, sourceCatInfo, destDir, destName, params, newFile );
|
---|
462 |
|
---|
463 | mycheck_noerr(osErr); // put up debug assert in debug builds
|
---|
464 |
|
---|
465 | return osErr;
|
---|
466 | }
|
---|
467 |
|
---|
468 | /*****************************************************************************/
|
---|
469 |
|
---|
470 | OSErr FSUsePBHCopyFile( const FSRef *srcFileRef,
|
---|
471 | const FSRef *dstDirectoryRef,
|
---|
472 | UniCharCount nameLength,
|
---|
473 | const UniChar *copyName, /* can be NULL (no rename during copy) */
|
---|
474 | TextEncoding textEncodingHint,
|
---|
475 | FSRef *newRef) /* can be NULL */
|
---|
476 | {
|
---|
477 | FSSpec srcFileSpec;
|
---|
478 | FSCatalogInfo catalogInfo;
|
---|
479 | GetVolParmsInfoBuffer volParmsInfo;
|
---|
480 | HParamBlockRec pb;
|
---|
481 | Str31 hfsName;
|
---|
482 | OSErr osErr;
|
---|
483 |
|
---|
484 | // get source FSSpec from source FSRef
|
---|
485 | osErr = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL);
|
---|
486 | if( osErr == noErr ) // Make sure the volume supports CopyFile
|
---|
487 | osErr = FSGetVolParms( srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer), &volParmsInfo, NULL);
|
---|
488 | if( osErr == noErr )
|
---|
489 | osErr = VolHasCopyFile(&volParmsInfo) ? noErr : paramErr;
|
---|
490 | if( osErr == noErr ) // get the destination vRefNum and dirID
|
---|
491 | osErr = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume | kFSCatInfoNodeID, &catalogInfo, NULL, NULL, NULL);
|
---|
492 | if( osErr == noErr ) // gather all the info needed
|
---|
493 | {
|
---|
494 | pb.copyParam.ioVRefNum = srcFileSpec.vRefNum;
|
---|
495 | pb.copyParam.ioDirID = srcFileSpec.parID;
|
---|
496 | pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name;
|
---|
497 | pb.copyParam.ioDstVRefNum = catalogInfo.volume;
|
---|
498 | pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID;
|
---|
499 | pb.copyParam.ioNewName = NULL;
|
---|
500 | if( copyName != NULL )
|
---|
501 | osErr = UnicodeNameGetHFSName(nameLength, copyName, textEncodingHint, false, hfsName);
|
---|
502 | pb.copyParam.ioCopyName = ( copyName != NULL && osErr == noErr ) ? hfsName : NULL;
|
---|
503 | }
|
---|
504 | if( osErr == noErr ) // tell the server to copy the object
|
---|
505 | osErr = PBHCopyFileSync(&pb);
|
---|
506 |
|
---|
507 | if( osErr == noErr && newRef != NULL )
|
---|
508 | {
|
---|
509 | myverify_noerr(FSMakeFSRef(pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID,
|
---|
510 | pb.copyParam.ioCopyName, newRef));
|
---|
511 | }
|
---|
512 |
|
---|
513 | if( osErr != paramErr ) // returning paramErr is ok, it means PBHCopyFileSync was not supported
|
---|
514 | mycheck_noerr(osErr); // put up debug assert in debug builds
|
---|
515 |
|
---|
516 | return osErr;
|
---|
517 | }
|
---|
518 |
|
---|
519 | /*****************************************************************************/
|
---|
520 |
|
---|
521 | // Copies a file referenced by source to the directory referenced by
|
---|
522 | // destDir. destName is the name the file should be given in the
|
---|
523 | // destination directory. sourceCatInfo is the catalogue info of
|
---|
524 | // the file, which is passed in as an optimization (we could get it
|
---|
525 | // by doing a FSGetCatalogInfo but the caller has already done that
|
---|
526 | // so we might as well take advantage of that).
|
---|
527 | //
|
---|
528 | OSErr DoCopyFile( const FSRef *source,
|
---|
529 | FSCatalogInfo *sourceCatInfo,
|
---|
530 | const FSRef *destDir,
|
---|
531 | ConstHFSUniStr255Param destName,
|
---|
532 | CopyParams *params,
|
---|
533 | FSRef *newRef)
|
---|
534 | {
|
---|
535 | FSRef dest;
|
---|
536 | FSPermissionInfo originalPermissions;
|
---|
537 | UTCDateTime originalCreateDate;
|
---|
538 | OSType originalFileType;
|
---|
539 | UInt16 originalNodeFlags;
|
---|
540 | OSErr osErr;
|
---|
541 |
|
---|
542 | // If we're copying to a drop folder, we won't be able to reset this
|
---|
543 | // information once the copy is done, so we don't mess it up in
|
---|
544 | // the first place. We still clear the locked bit though; items dropped
|
---|
545 | // into a drop folder always become unlocked.
|
---|
546 | if (!params->copyingToDropFolder)
|
---|
547 | {
|
---|
548 | // Remember to clear the file's type, so the Finder doesn't
|
---|
549 | // look at the file until we're done.
|
---|
550 | originalFileType = ((FInfo *) &sourceCatInfo->finderInfo)->fdType;
|
---|
551 | ((FInfo *) &sourceCatInfo->finderInfo)->fdType = kFirstMagicBusyFiletype;
|
---|
552 |
|
---|
553 | // Remember and clear the file's locked status, so that we can
|
---|
554 | // actually write the forks we're about to create.
|
---|
555 | originalNodeFlags = sourceCatInfo->nodeFlags;
|
---|
556 |
|
---|
557 | // Set the file's creation date to kMagicBusyCreationDate,
|
---|
558 | // remembering the old value for restoration later.
|
---|
559 | originalCreateDate = sourceCatInfo->createDate;
|
---|
560 | sourceCatInfo->createDate = params->magicBusyCreateDate;
|
---|
561 | }
|
---|
562 | sourceCatInfo->nodeFlags &= ~kFSNodeLockedMask;
|
---|
563 |
|
---|
564 | // we need to have user level read/write/execute access to the file we are going to create
|
---|
565 | // otherwise FSCreateFileUnicode will return -5000 (afpAccessDenied),
|
---|
566 | // and the FSRef returned will be invalid, yet the file is created (size 0k)... bug?
|
---|
567 | originalPermissions = *((FSPermissionInfo*)sourceCatInfo->permissions);
|
---|
568 | ((FSPermissionInfo*)sourceCatInfo->permissions)->mode |= kRWXUserAccessMask;
|
---|
569 |
|
---|
570 | // Classic only supports 9.1 and higher, so we don't have to worry about 2397324
|
---|
571 | osErr = FSCreateFileUnicode(destDir, destName->length, destName->unicode, kFSCatInfoSettableInfo, sourceCatInfo, &dest, NULL);
|
---|
572 | if( osErr == noErr ) // Copy the forks over to the new file
|
---|
573 | osErr = CopyItemsForks(source, &dest, params);
|
---|
574 |
|
---|
575 | // Restore the original file type, creation and modification dates,
|
---|
576 | // locked status and permissions.
|
---|
577 | // This is one of the places where we need to handle drop
|
---|
578 | // folders as a special case because this FSSetCatalogInfo will fail for
|
---|
579 | // an item in a drop folder, so we don't even attempt it.
|
---|
580 | if (osErr == noErr && !params->copyingToDropFolder)
|
---|
581 | {
|
---|
582 | ((FInfo *) &sourceCatInfo->finderInfo)->fdType = originalFileType;
|
---|
583 | sourceCatInfo->createDate = originalCreateDate;
|
---|
584 | sourceCatInfo->nodeFlags = originalNodeFlags;
|
---|
585 | *((FSPermissionInfo*)sourceCatInfo->permissions) = originalPermissions;
|
---|
586 |
|
---|
587 | osErr = FSSetCatalogInfo(&dest, kFSCatInfoSettableInfo, sourceCatInfo);
|
---|
588 | }
|
---|
589 |
|
---|
590 | // If we created the file and the copy failed, try to clean up by
|
---|
591 | // deleting the file we created. We do this because, while it's
|
---|
592 | // possible for the copy to fail halfway through and the File Manager
|
---|
593 | // doesn't really clean up that well, we *really* don't wan't
|
---|
594 | // any half-created files being left around.
|
---|
595 | // if the file already existed, we don't want to delete it
|
---|
596 | //
|
---|
597 | // Note that there are cases where the assert can fire which are not
|
---|
598 | // errors (for example, if the destination is in a drop folder) but
|
---|
599 | // I'll leave it in anyway because I'm interested in discovering those
|
---|
600 | // cases. Note that, if this fires and we're running MP, current versions
|
---|
601 | // of MacsBug won't catch the exception and the MP task will terminate
|
---|
602 | // with a kMPTaskAbortedErr error.
|
---|
603 | if (osErr != noErr && osErr != dupFNErr )
|
---|
604 | myverify_noerr( FSDeleteObjects(&dest) );
|
---|
605 | else if( newRef != NULL ) // if everything was fine, then return the new file
|
---|
606 | *newRef = dest;
|
---|
607 |
|
---|
608 | mycheck_noerr(osErr); // put up debug assert in debug builds
|
---|
609 |
|
---|
610 | return osErr;
|
---|
611 | }
|
---|
612 |
|
---|
613 | /*****************************************************************************/
|
---|
614 |
|
---|
615 | #pragma mark ----- Copy Folders -----
|
---|
616 |
|
---|
617 | OSErr FSCopyFolder( const FSRef *source, const FSRef *destDir, const HFSUniStr255 *destName,
|
---|
618 | CopyParams* copyParams, FilterParams *filterParams, ItemCount maxLevels, FSRef* newDir)
|
---|
619 | {
|
---|
620 | FSCopyObjectGlobals theGlobals;
|
---|
621 |
|
---|
622 | theGlobals.ref = *source;
|
---|
623 | theGlobals.destRef = *destDir;
|
---|
624 | theGlobals.copyParams = copyParams;
|
---|
625 | theGlobals.filterParams = filterParams;
|
---|
626 | theGlobals.maxLevels = maxLevels;
|
---|
627 | theGlobals.currentLevel = 0;
|
---|
628 | theGlobals.quitFlag = false;
|
---|
629 | theGlobals.containerChanged = false;
|
---|
630 | theGlobals.result = ( source != NULL && destDir != NULL &&
|
---|
631 | copyParams != NULL && filterParams != NULL ) ?
|
---|
632 | noErr : paramErr;
|
---|
633 | theGlobals.actualObjects = 0;
|
---|
634 |
|
---|
635 | // here we go into recursion land...
|
---|
636 | if( theGlobals.result == noErr )
|
---|
637 | theGlobals.result = FSCopyFolderLevel(&theGlobals, destName);
|
---|
638 |
|
---|
639 | if( theGlobals.result == noErr && newDir != NULL)
|
---|
640 | *newDir = theGlobals.ref;
|
---|
641 |
|
---|
642 | // Call the IterateFilterProc _after_ the new folder is created
|
---|
643 | // even if we failed...
|
---|
644 | if( filterParams->filterProcPtr != NULL )
|
---|
645 | {
|
---|
646 | (void) CallCopyObjectFilterProc(filterParams->filterProcPtr, theGlobals.containerChanged,
|
---|
647 | theGlobals.currentLevel, theGlobals.result, &theGlobals.catalogInfo,
|
---|
648 | &theGlobals.ref, filterParams->fileSpecPtr,
|
---|
649 | filterParams->fileNamePtr, filterParams->yourDataPtr);
|
---|
650 | }
|
---|
651 |
|
---|
652 | mycheck_noerr(theGlobals.result); // put up debug assert in debug builds
|
---|
653 |
|
---|
654 | return ( theGlobals.result );
|
---|
655 | }
|
---|
656 |
|
---|
657 | /*****************************************************************************/
|
---|
658 |
|
---|
659 | OSErr FSCopyFolderLevel( FSCopyObjectGlobals *theGlobals, const HFSUniStr255 *destName )
|
---|
660 | {
|
---|
661 | // If maxLevels is zero, we aren't checking levels
|
---|
662 | // If currentLevel < maxLevels, look at this level
|
---|
663 | if ( (theGlobals->maxLevels == 0) ||
|
---|
664 | (theGlobals->currentLevel < theGlobals->maxLevels) )
|
---|
665 | {
|
---|
666 | FSRef newDirRef;
|
---|
667 | UTCDateTime originalCreateDate;
|
---|
668 | FSPermissionInfo originalPermissions;
|
---|
669 | FSIterator iterator;
|
---|
670 | FilterParams *filterPtr = theGlobals->filterParams;
|
---|
671 |
|
---|
672 | // get the info we need on the source file...
|
---|
673 | theGlobals->result = FSGetCatalogInfo( &theGlobals->ref, filterPtr->whichInfo,
|
---|
674 | &theGlobals->catalogInfo, &filterPtr->fileName,
|
---|
675 | NULL, NULL);
|
---|
676 |
|
---|
677 | if (theGlobals->currentLevel == 0 && destName)
|
---|
678 | filterPtr->fileName = *destName;
|
---|
679 |
|
---|
680 | // Clear the "inited" bit so that the Finder positions the icon for us.
|
---|
681 | ((FInfo *)(theGlobals->catalogInfo.finderInfo))->fdFlags &= ~kHasBeenInited;
|
---|
682 |
|
---|
683 | // Set the folder's creation date to kMagicBusyCreationDate
|
---|
684 | // so that the Finder doesn't mess with the folder while
|
---|
685 | // it's copying. We remember the old value for restoration
|
---|
686 | // later. We only do this if we're not copying to a drop
|
---|
687 | // folder, because if we are copying to a drop folder we don't
|
---|
688 | // have the opportunity to reset the information at the end of
|
---|
689 | // this routine.
|
---|
690 | if ( theGlobals->result == noErr && !theGlobals->copyParams->copyingToDropFolder)
|
---|
691 | {
|
---|
692 | originalCreateDate = theGlobals->catalogInfo.createDate;
|
---|
693 | theGlobals->catalogInfo.createDate = theGlobals->copyParams->magicBusyCreateDate;
|
---|
694 | }
|
---|
695 |
|
---|
696 | // we need to have user level read/write/execute access to the folder we are going to create,
|
---|
697 | // otherwise FSCreateDirectoryUnicode will return -5000 (afpAccessDenied),
|
---|
698 | // and the FSRef returned will be invalid, yet the folder is created... bug?
|
---|
699 | originalPermissions = *((FSPermissionInfo*)theGlobals->catalogInfo.permissions);
|
---|
700 | ((FSPermissionInfo*)theGlobals->catalogInfo.permissions)->mode |= kRWXUserAccessMask;
|
---|
701 |
|
---|
702 | // create the new directory
|
---|
703 | if( theGlobals->result == noErr )
|
---|
704 | {
|
---|
705 | theGlobals->result = FSCreateDirectoryUnicode( &theGlobals->destRef, filterPtr->fileName.length,
|
---|
706 | filterPtr->fileName.unicode, kFSCatInfoSettableInfo,
|
---|
707 | &theGlobals->catalogInfo, &newDirRef,
|
---|
708 | &filterPtr->fileSpec, NULL);
|
---|
709 | }
|
---|
710 |
|
---|
711 | ++theGlobals->currentLevel; // setup to go to the next level
|
---|
712 |
|
---|
713 | // With the new APIs, folders can have forks as well as files. Before
|
---|
714 | // we start copying items in the folder, we must copy over the forks
|
---|
715 | if( theGlobals->result == noErr )
|
---|
716 | theGlobals->result = CopyItemsForks(&theGlobals->ref, &newDirRef, theGlobals->copyParams);
|
---|
717 | if( theGlobals->result == noErr ) // Open FSIterator for flat access to theGlobals->ref
|
---|
718 | theGlobals->result = FSOpenIterator(&theGlobals->ref, kFSIterateFlat, &iterator);
|
---|
719 | if( theGlobals->result == noErr )
|
---|
720 | {
|
---|
721 | OSErr osErr;
|
---|
722 |
|
---|
723 | // Call FSGetCatalogInfoBulk in loop to get all items in the container
|
---|
724 | do
|
---|
725 | {
|
---|
726 | theGlobals->result = FSGetCatalogInfoBulk( iterator, 1, &theGlobals->actualObjects,
|
---|
727 | &theGlobals->containerChanged, filterPtr->whichInfo,
|
---|
728 | &theGlobals->catalogInfo, &theGlobals->ref,
|
---|
729 | filterPtr->fileSpecPtr, &filterPtr->fileName);
|
---|
730 | if ( ( (theGlobals->result == noErr) || (theGlobals->result == errFSNoMoreItems) ) &&
|
---|
731 | ( theGlobals->actualObjects != 0 ) )
|
---|
732 | {
|
---|
733 | // Any errors in here will be passed to the filter proc
|
---|
734 | // we don't want an error in here to prematurely
|
---|
735 | // cancel the recursive copy, leaving a half filled directory
|
---|
736 |
|
---|
737 | // is the new object a directory?
|
---|
738 | if ( (theGlobals->catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0 )
|
---|
739 | { // yes
|
---|
740 | theGlobals->destRef = newDirRef;
|
---|
741 | osErr = FSCopyFolderLevel(theGlobals, NULL);
|
---|
742 | theGlobals->result = noErr; // don't want one silly mistake to kill the party...
|
---|
743 | }
|
---|
744 | else // no
|
---|
745 | {
|
---|
746 | osErr = CopyFile( &theGlobals->ref, &theGlobals->catalogInfo,
|
---|
747 | &newDirRef, &filterPtr->fileName,
|
---|
748 | theGlobals->copyParams, &theGlobals->ref);
|
---|
749 | }
|
---|
750 |
|
---|
751 | // Call the filter proc _after_ the file/folder was created completly
|
---|
752 | if( filterPtr->filterProcPtr != NULL && !theGlobals->quitFlag )
|
---|
753 | {
|
---|
754 | theGlobals->quitFlag = CallCopyObjectFilterProc(filterPtr->filterProcPtr,
|
---|
755 | theGlobals->containerChanged, theGlobals->currentLevel,
|
---|
756 | osErr, &theGlobals->catalogInfo,
|
---|
757 | &theGlobals->ref, filterPtr->fileSpecPtr,
|
---|
758 | filterPtr->fileNamePtr, filterPtr->yourDataPtr);
|
---|
759 | }
|
---|
760 | }
|
---|
761 | } while ( ( theGlobals->result == noErr ) && ( !theGlobals->quitFlag ) );
|
---|
762 |
|
---|
763 | // Close the FSIterator (closing an open iterator should never fail)
|
---|
764 | (void) FSCloseIterator(iterator);
|
---|
765 | }
|
---|
766 |
|
---|
767 | // errFSNoMoreItems is OK - it only means we hit the end of this level
|
---|
768 | // afpAccessDenied is OK, too - it only means we cannot see inside a directory
|
---|
769 | if ( (theGlobals->result == errFSNoMoreItems) || (theGlobals->result == afpAccessDenied) )
|
---|
770 | theGlobals->result = noErr;
|
---|
771 |
|
---|
772 | // store away the name, and an FSSpec and FSRef of the new directory
|
---|
773 | // for use in filter proc one level up...
|
---|
774 | if( theGlobals->result == noErr )
|
---|
775 | {
|
---|
776 | theGlobals->ref = newDirRef;
|
---|
777 | theGlobals->result = FSGetCatalogInfo(&newDirRef, kFSCatInfoNone, NULL,
|
---|
778 | &filterPtr->fileName, &filterPtr->fileSpec, NULL);
|
---|
779 | }
|
---|
780 |
|
---|
781 | // Return to previous level as we leave
|
---|
782 | --theGlobals->currentLevel;
|
---|
783 |
|
---|
784 | // Reset the modification dates and permissions, except when copying to a drop folder
|
---|
785 | // where this won't work.
|
---|
786 | if (theGlobals->result == noErr && ! theGlobals->copyParams->copyingToDropFolder)
|
---|
787 | {
|
---|
788 | theGlobals->catalogInfo.createDate = originalCreateDate;
|
---|
789 | *((FSPermissionInfo*)theGlobals->catalogInfo.permissions) = originalPermissions;
|
---|
790 | theGlobals->result = FSSetCatalogInfo(&newDirRef, kFSCatInfoCreateDate
|
---|
791 | | kFSCatInfoAttrMod
|
---|
792 | | kFSCatInfoContentMod
|
---|
793 | | kFSCatInfoPermissions, &theGlobals->catalogInfo);
|
---|
794 | }
|
---|
795 |
|
---|
796 | // If we created the folder and the copy failed, try to clean up by
|
---|
797 | // deleting the folder we created. We do this because, while it's
|
---|
798 | // possible for the copy to fail halfway through and the File Manager
|
---|
799 | // doesn't really clean up that well, we *really* don't wan't any
|
---|
800 | // half-created files/folders being left around.
|
---|
801 | // if the file already existed, we don't want to delete it
|
---|
802 | if( theGlobals->result != noErr && theGlobals->result != dupFNErr )
|
---|
803 | myverify_noerr( FSDeleteObjects(&newDirRef) );
|
---|
804 | }
|
---|
805 |
|
---|
806 | mycheck_noerr( theGlobals->result ); // put up debug assert in debug builds
|
---|
807 |
|
---|
808 | return theGlobals->result;
|
---|
809 | }
|
---|
810 |
|
---|
811 | /*****************************************************************************/
|
---|
812 |
|
---|
813 | // Determines whether the destination directory is equal to the source
|
---|
814 | // item, or whether it's nested inside the source item. Returns a
|
---|
815 | // errFSDestInsideSource if that's the case. We do this to prevent
|
---|
816 | // endless recursion while copying.
|
---|
817 | //
|
---|
818 | OSErr CheckForDestInsideSource(const FSRef *source, const FSRef *destDir)
|
---|
819 | {
|
---|
820 | FSRef thisDir = *destDir;
|
---|
821 | FSCatalogInfo thisDirInfo;
|
---|
822 | Boolean done = false;
|
---|
823 | OSErr osErr;
|
---|
824 |
|
---|
825 | do
|
---|
826 | {
|
---|
827 | osErr = FSCompareFSRefs(source, &thisDir);
|
---|
828 | if (osErr == noErr)
|
---|
829 | osErr = errFSDestInsideSource;
|
---|
830 | else if (osErr == diffVolErr)
|
---|
831 | {
|
---|
832 | osErr = noErr;
|
---|
833 | done = true;
|
---|
834 | }
|
---|
835 | else if (osErr == errFSRefsDifferent)
|
---|
836 | {
|
---|
837 | // This is somewhat tricky. We can ask for the parent of thisDir
|
---|
838 | // by setting the parentRef parameter to FSGetCatalogInfo but, if
|
---|
839 | // thisDir is the volume's FSRef, this will give us back junk.
|
---|
840 | // So we also ask for the parent's dir ID to be returned in the
|
---|
841 | // FSCatalogInfo record, and then check that against the node
|
---|
842 | // ID of the root's parent (ie 1). If we match that, we've made
|
---|
843 | // it to the top of the hierarchy without hitting source, so
|
---|
844 | // we leave with no error.
|
---|
845 |
|
---|
846 | osErr = FSGetCatalogInfo(&thisDir, kFSCatInfoParentDirID, &thisDirInfo, NULL, NULL, &thisDir);
|
---|
847 | if( ( osErr == noErr ) && ( thisDirInfo.parentDirID == fsRtParID ) )
|
---|
848 | done = true;
|
---|
849 | }
|
---|
850 | } while ( osErr == noErr && ! done );
|
---|
851 |
|
---|
852 | mycheck_noerr( osErr ); // put up debug assert in debug builds
|
---|
853 |
|
---|
854 | return osErr;
|
---|
855 | }
|
---|
856 |
|
---|
857 | /*****************************************************************************/
|
---|
858 |
|
---|
859 | #pragma mark ----- Copy Forks -----
|
---|
860 |
|
---|
861 | OSErr CopyItemsForks(const FSRef *source, const FSRef *dest, CopyParams *params)
|
---|
862 | {
|
---|
863 | ForkTracker dataFork,
|
---|
864 | rsrcFork;
|
---|
865 | ForkTrackerPtr otherForks;
|
---|
866 | ItemCount otherForksCount,
|
---|
867 | thisForkIndex;
|
---|
868 | OSErr osErr;
|
---|
869 |
|
---|
870 | dataFork.forkDestRefNum = 0;
|
---|
871 | rsrcFork.forkDestRefNum = 0;
|
---|
872 | otherForks = NULL;
|
---|
873 | otherForksCount = 0;
|
---|
874 |
|
---|
875 | // Get the constant names for the resource and data fork, which
|
---|
876 | // we're going to need inside the copy engine.
|
---|
877 | osErr = FSGetDataForkName(&dataFork.forkName);
|
---|
878 | if( osErr == noErr )
|
---|
879 | osErr = FSGetResourceForkName(&rsrcFork.forkName);
|
---|
880 | if( osErr == noErr ) // First determine the list of forks that the source has.
|
---|
881 | osErr = CalculateForksToCopy(source, &dataFork, &rsrcFork, &otherForks, &otherForksCount);
|
---|
882 | if (osErr == noErr)
|
---|
883 | {
|
---|
884 | // If we're copying into a drop folder, open up all of those forks.
|
---|
885 | // We have to do this because, once we've starting writing to a fork
|
---|
886 | // in a drop folder, we can't open any more forks.
|
---|
887 | //
|
---|
888 | // We only do this if we're copying into a drop folder in order
|
---|
889 | // to conserve FCBs in the more common, non-drop folder case.
|
---|
890 |
|
---|
891 | if (params->copyingToDropFolder)
|
---|
892 | osErr = OpenAllForks(dest, &dataFork, &rsrcFork, otherForks, otherForksCount);
|
---|
893 |
|
---|
894 | // Copy each fork.
|
---|
895 | if (osErr == noErr && (dataFork.forkSize != 0)) // copy data fork
|
---|
896 | osErr = CopyFork(source, dest, &dataFork, params);
|
---|
897 | if (osErr == noErr && (rsrcFork.forkSize != 0)) // copy resource fork
|
---|
898 | osErr = CopyFork(source, dest, &rsrcFork, params);
|
---|
899 | if (osErr == noErr) { // copy other forks
|
---|
900 | for (thisForkIndex = 0; thisForkIndex < otherForksCount && osErr == noErr; thisForkIndex++)
|
---|
901 | osErr = CopyFork(source,dest, &otherForks[thisForkIndex], params);
|
---|
902 | }
|
---|
903 |
|
---|
904 | // Close any forks that might be left open. Note that we have to call
|
---|
905 | // this regardless of an error. Also note that this only closes forks
|
---|
906 | // that were opened by OpenAllForks. If we're not copying into a drop
|
---|
907 | // folder, the forks are opened and closed by CopyFork.
|
---|
908 | {
|
---|
909 | OSErr osErr2 = CloseAllForks(dataFork.forkDestRefNum, rsrcFork.forkDestRefNum, otherForks, otherForksCount);
|
---|
910 | mycheck_noerr(osErr2);
|
---|
911 | if (osErr == noErr)
|
---|
912 | osErr = osErr2;
|
---|
913 | }
|
---|
914 | }
|
---|
915 |
|
---|
916 | // Clean up.
|
---|
917 | if (otherForks != NULL)
|
---|
918 | DisposePtr((char*)otherForks);
|
---|
919 |
|
---|
920 | mycheck_noerr( osErr ); // put up debug assert in debug builds
|
---|
921 |
|
---|
922 | return osErr;
|
---|
923 | }
|
---|
924 |
|
---|
925 | /*****************************************************************************/
|
---|
926 |
|
---|
927 | // Open all the forks of the file. We need to do this when we're copying
|
---|
928 | // into a drop folder, where you must open all the forks before starting
|
---|
929 | // to write to any of them.
|
---|
930 | //
|
---|
931 | // IMPORTANT: If it fails, this routine won't close forks that opened successfully.
|
---|
932 | // You must call CloseAllForks regardless of whether this routine returns an error.
|
---|
933 | OSErr OpenAllForks( const FSRef *dest,
|
---|
934 | const ForkTrackerPtr dataFork,
|
---|
935 | const ForkTrackerPtr rsrcFork,
|
---|
936 | ForkTrackerPtr otherForks,
|
---|
937 | ItemCount otherForksCount)
|
---|
938 | {
|
---|
939 | ItemCount thisForkIndex;
|
---|
940 | OSErr osErr = noErr;
|
---|
941 |
|
---|
942 | // Open the resource and data forks as a special case, if they exist in this object
|
---|
943 | if (dataFork->forkSize != 0) // Data fork never needs to be created, so I don't have to FSCreateFork it here.
|
---|
944 | osErr = FSOpenFork(dest, dataFork->forkName.length, dataFork->forkName.unicode, fsWrPerm, &dataFork->forkDestRefNum);
|
---|
945 | if (osErr == noErr && rsrcFork->forkSize != 0) // Resource fork never needs to be created, so I don't have to FSCreateFork it here.
|
---|
946 | osErr = FSOpenFork(dest, rsrcFork->forkName.length, rsrcFork->forkName.unicode, fsWrPerm, &rsrcFork->forkDestRefNum);
|
---|
947 |
|
---|
948 | if (osErr == noErr && otherForks != NULL && otherForksCount > 0) // Open the other forks.
|
---|
949 | {
|
---|
950 | for (thisForkIndex = 0; thisForkIndex < otherForksCount && osErr == noErr; thisForkIndex++)
|
---|
951 | {
|
---|
952 | // Create the fork. Swallow afpAccessDenied because this operation
|
---|
953 | // causes the external file system compatibility shim in Mac OS 9 to
|
---|
954 | // generate a GetCatInfo request to the AppleShare external file system,
|
---|
955 | // which in turn causes an AFP GetFileDirParms request on the wire,
|
---|
956 | // which the AFP server bounces with afpAccessDenied because the file
|
---|
957 | // is in a drop folder. As there's no native support for non-classic
|
---|
958 | // forks in current AFP, there's no way I can decide how I should
|
---|
959 | // handle this in a non-test case. So I just swallow the error and
|
---|
960 | // hope that when native AFP support arrives, the right thing will happen.
|
---|
961 | osErr = FSCreateFork(dest, otherForks[thisForkIndex].forkName.length, otherForks[thisForkIndex].forkName.unicode);
|
---|
962 | if (osErr == noErr || osErr == afpAccessDenied)
|
---|
963 | osErr = noErr;
|
---|
964 |
|
---|
965 | // Previously I avoided opening up the fork if the fork if the
|
---|
966 | // length was empty, but that confused CopyFork into thinking
|
---|
967 | // this wasn't a drop folder copy, so I decided to simply avoid
|
---|
968 | // this trivial optimization. In drop folders, we always open
|
---|
969 | // all forks.
|
---|
970 | if (osErr == noErr)
|
---|
971 | osErr = FSOpenFork(dest, otherForks[thisForkIndex].forkName.length, otherForks[thisForkIndex].forkName.unicode, fsWrPerm, &otherForks[thisForkIndex].forkDestRefNum);
|
---|
972 | }
|
---|
973 | }
|
---|
974 |
|
---|
975 | mycheck_noerr( osErr ); // put up debug assert in debug builds
|
---|
976 |
|
---|
977 | return osErr;
|
---|
978 | }
|
---|
979 |
|
---|
980 | /*****************************************************************************/
|
---|
981 |
|
---|
982 | // Copies the fork whose name is forkName from source to dest.
|
---|
983 | // A refnum for the destination fork may be supplied in forkDestRefNum.
|
---|
984 | // If forkDestRefNum is 0, we must open the destination fork ourselves,
|
---|
985 | // otherwise it has been opened for us and we shouldn't close it.
|
---|
986 | OSErr CopyFork( const FSRef *source, const FSRef *dest, const ForkTrackerPtr sourceFork, const CopyParams *params)
|
---|
987 | {
|
---|
988 | UInt64 bytesRemaining;
|
---|
989 | UInt64 bytesToReadThisTime;
|
---|
990 | UInt64 bytesToWriteThisTime;
|
---|
991 | SInt16 sourceRef;
|
---|
992 | SInt16 destRef;
|
---|
993 | OSErr osErr = noErr;
|
---|
994 | OSErr osErr2 = noErr;
|
---|
995 |
|
---|
996 | // If we haven't been passed in a sourceFork->forkDestRefNum (which basically
|
---|
997 | // means we're copying into a non-drop folder), create the destination
|
---|
998 | // fork. We have to do this regardless of whether sourceFork->forkSize is
|
---|
999 | // 0, because we want to preserve empty forks.
|
---|
1000 | if (sourceFork->forkDestRefNum == 0)
|
---|
1001 | {
|
---|
1002 | osErr = FSCreateFork(dest, sourceFork->forkName.length, sourceFork->forkName.unicode);
|
---|
1003 |
|
---|
1004 | // Mac OS 9.0 has a bug (in the AppleShare external file system,
|
---|
1005 | // I think) [2410374] that causes FSCreateFork to return an errFSForkExists
|
---|
1006 | // error even though the fork is empty. The following code swallows
|
---|
1007 | // the error (which is harmless) in that case.
|
---|
1008 | if (osErr == errFSForkExists && !params->copyingToLocalVolume)
|
---|
1009 | osErr = noErr;
|
---|
1010 | }
|
---|
1011 |
|
---|
1012 | // The remainder of this code only applies if there is actual data
|
---|
1013 | // in the source fork.
|
---|
1014 |
|
---|
1015 | if (osErr == noErr && sourceFork->forkSize != 0) {
|
---|
1016 |
|
---|
1017 | // Prepare for failure.
|
---|
1018 |
|
---|
1019 | sourceRef = 0;
|
---|
1020 | destRef = 0;
|
---|
1021 |
|
---|
1022 | // Open up the destination fork, if we're asked to, otherwise
|
---|
1023 | // just use the passed in sourceFork->forkDestRefNum.
|
---|
1024 | if( sourceFork->forkDestRefNum == 0 )
|
---|
1025 | osErr = FSOpenFork(dest, sourceFork->forkName.length, sourceFork->forkName.unicode, fsWrPerm, &destRef);
|
---|
1026 | else
|
---|
1027 | destRef = sourceFork->forkDestRefNum;
|
---|
1028 |
|
---|
1029 | // Open up the source fork.
|
---|
1030 | if (osErr == noErr)
|
---|
1031 | osErr = FSOpenFork(source, sourceFork->forkName.length, sourceFork->forkName.unicode, fsRdPerm, &sourceRef);
|
---|
1032 |
|
---|
1033 | // Here we create space for the entire fork on the destination volume.
|
---|
1034 | // FSAllocateFork has the right semantics on both traditional Mac OS
|
---|
1035 | // and Mac OS X. On traditional Mac OS it will allocate space for the
|
---|
1036 | // file in one hit without any other special action. On Mac OS X,
|
---|
1037 | // FSAllocateFork is preferable to FSSetForkSize because it prevents
|
---|
1038 | // the system from zero filling the bytes that were added to the end
|
---|
1039 | // of the fork (which would be waste becasue we're about to write over
|
---|
1040 | // those bytes anyway.
|
---|
1041 | if( osErr == noErr )
|
---|
1042 | osErr = FSAllocateFork(destRef, kFSAllocNoRoundUpMask, fsFromStart, 0, sourceFork->forkSize, NULL);
|
---|
1043 |
|
---|
1044 | // Copy the file from the source to the destination in chunks of
|
---|
1045 | // no more than params->copyBufferSize bytes. This is fairly
|
---|
1046 | // boring code except for the bytesToReadThisTime/bytesToWriteThisTime
|
---|
1047 | // distinction. On the last chunk, we round bytesToWriteThisTime
|
---|
1048 | // up to the next 512 byte boundary and then, after we exit the loop,
|
---|
1049 | // we set the file's EOF back to the real location (if the fork size
|
---|
1050 | // is not a multiple of 512 bytes).
|
---|
1051 | //
|
---|
1052 | // This technique works around a 'bug' in the traditional Mac OS File Manager,
|
---|
1053 | // where the File Manager will put the last 512-byte block of a large write into
|
---|
1054 | // the cache (even if we specifically request no caching) if that block is not
|
---|
1055 | // full. If the block goes into the cache it will eventually have to be
|
---|
1056 | // flushed, which causes sub-optimal disk performance.
|
---|
1057 | //
|
---|
1058 | // This is only done if the destination volume is local. For a network
|
---|
1059 | // volume, it's better to just write the last bytes directly.
|
---|
1060 | //
|
---|
1061 | // This is extreme over-optimization given the other limits of this
|
---|
1062 | // sample, but I will hopefully get to the other limits eventually.
|
---|
1063 | bytesRemaining = sourceFork->forkSize;
|
---|
1064 | while (osErr == noErr && bytesRemaining != 0)
|
---|
1065 | {
|
---|
1066 | if (bytesRemaining > params->copyBufferSize)
|
---|
1067 | {
|
---|
1068 | bytesToReadThisTime = params->copyBufferSize;
|
---|
1069 | bytesToWriteThisTime = bytesToReadThisTime;
|
---|
1070 | }
|
---|
1071 | else
|
---|
1072 | {
|
---|
1073 | bytesToReadThisTime = bytesRemaining;
|
---|
1074 | bytesToWriteThisTime = (params->copyingToLocalVolume) ?
|
---|
1075 | (bytesRemaining + 0x01FF) & ~0x01FF :
|
---|
1076 | bytesRemaining;
|
---|
1077 | }
|
---|
1078 |
|
---|
1079 | osErr = FSReadFork(sourceRef, fsAtMark + noCacheMask, 0, bytesToReadThisTime, params->copyBuffer, NULL);
|
---|
1080 | if (osErr == noErr)
|
---|
1081 | osErr = FSWriteFork(destRef, fsAtMark + noCacheMask, 0, bytesToWriteThisTime, params->copyBuffer, NULL);
|
---|
1082 | if (osErr == noErr)
|
---|
1083 | bytesRemaining -= bytesToReadThisTime;
|
---|
1084 | }
|
---|
1085 |
|
---|
1086 | if (osErr == noErr && (params->copyingToLocalVolume && ((sourceFork->forkSize & 0x01FF) != 0)) )
|
---|
1087 | osErr = FSSetForkSize(destRef, fsFromStart, sourceFork->forkSize);
|
---|
1088 |
|
---|
1089 | // Clean up.
|
---|
1090 | if (sourceRef != 0)
|
---|
1091 | {
|
---|
1092 | osErr2 = FSCloseFork(sourceRef);
|
---|
1093 | mycheck_noerr(osErr2);
|
---|
1094 | if (osErr == noErr)
|
---|
1095 | osErr = osErr2;
|
---|
1096 | }
|
---|
1097 |
|
---|
1098 | // Only close destRef if we were asked to open it (ie sourceFork->forkDestRefNum == 0) and
|
---|
1099 | // we actually managed to open it (ie destRef != 0).
|
---|
1100 | if (sourceFork->forkDestRefNum == 0 && destRef != 0)
|
---|
1101 | {
|
---|
1102 | osErr2 = FSCloseFork(destRef);
|
---|
1103 | mycheck_noerr(osErr2);
|
---|
1104 | if (osErr == noErr)
|
---|
1105 | osErr = osErr2;
|
---|
1106 | }
|
---|
1107 | }
|
---|
1108 |
|
---|
1109 | mycheck_noerr( osErr ); // put up debug assert in debug builds
|
---|
1110 |
|
---|
1111 | return osErr;
|
---|
1112 | }
|
---|
1113 |
|
---|
1114 | /*****************************************************************************/
|
---|
1115 |
|
---|
1116 | // Close all the forks that might have been opened by OpenAllForks.
|
---|
1117 | OSErr CloseAllForks(SInt16 dataRefNum, SInt16 rsrcRefNum, ForkTrackerPtr otherForks, ItemCount otherForksCount)
|
---|
1118 | {
|
---|
1119 | ItemCount thisForkIndex;
|
---|
1120 | OSErr osErr = noErr,
|
---|
1121 | osErr2;
|
---|
1122 |
|
---|
1123 | if (dataRefNum != 0)
|
---|
1124 | {
|
---|
1125 | osErr2 = FSCloseFork(dataRefNum);
|
---|
1126 | mycheck_noerr(osErr2);
|
---|
1127 | if (osErr == noErr)
|
---|
1128 | osErr = osErr2;
|
---|
1129 | }
|
---|
1130 | if (rsrcRefNum != 0)
|
---|
1131 | {
|
---|
1132 | osErr2 = FSCloseFork(rsrcRefNum);
|
---|
1133 | mycheck_noerr(osErr2);
|
---|
1134 | if (osErr == noErr)
|
---|
1135 | osErr = osErr2;
|
---|
1136 | }
|
---|
1137 | if( otherForks != NULL && otherForksCount > 0 )
|
---|
1138 | {
|
---|
1139 | for (thisForkIndex = 0; thisForkIndex < otherForksCount; thisForkIndex++)
|
---|
1140 | {
|
---|
1141 | if (otherForks[thisForkIndex].forkDestRefNum != 0)
|
---|
1142 | {
|
---|
1143 | osErr2 = FSCloseFork(otherForks[thisForkIndex].forkDestRefNum);
|
---|
1144 | mycheck_noerr(osErr2);
|
---|
1145 | if (osErr == noErr)
|
---|
1146 | osErr = osErr2;
|
---|
1147 | }
|
---|
1148 | }
|
---|
1149 | }
|
---|
1150 |
|
---|
1151 | mycheck_noerr( osErr ); // put up debug assert in debug builds
|
---|
1152 |
|
---|
1153 | return osErr;
|
---|
1154 | }
|
---|
1155 |
|
---|
1156 | /*****************************************************************************/
|
---|
1157 |
|
---|
1158 | // This routine determines the list of forks that a file has.
|
---|
1159 | // dataFork is populated if the file has a data fork.
|
---|
1160 | // rsrcFork is populated if the file has a resource fork.
|
---|
1161 | // otherForksParam is set to point to a memory block allocated with
|
---|
1162 | // NewPtr if the file has forks beyond the resource and data
|
---|
1163 | // forks. You must free that block with DisposePtr. otherForksCountParam
|
---|
1164 | // is set to the number of forks in the otherForksParam
|
---|
1165 | // array. This count does *not* include the resource and data forks.
|
---|
1166 | OSErr CalculateForksToCopy( const FSRef *source,
|
---|
1167 | const ForkTrackerPtr dataFork,
|
---|
1168 | const ForkTrackerPtr rsrcFork,
|
---|
1169 | ForkTrackerPtr *otherForksParam,
|
---|
1170 | ItemCount *otherForksCountParam)
|
---|
1171 | {
|
---|
1172 | Boolean done;
|
---|
1173 | CatPositionRec iterator;
|
---|
1174 | HFSUniStr255 thisForkName;
|
---|
1175 | SInt64 thisForkSize;
|
---|
1176 | ForkTrackerPtr otherForks;
|
---|
1177 | ItemCount otherForksCount;
|
---|
1178 | ItemCount otherForksMemoryBlockCount;
|
---|
1179 | OSErr osErr = ( (source != NULL) && (dataFork != NULL) &&
|
---|
1180 | (rsrcFork != NULL) && (otherForksParam != NULL) &&
|
---|
1181 | (otherForksCountParam != NULL) ) ?
|
---|
1182 | noErr : paramErr;
|
---|
1183 |
|
---|
1184 | dataFork->forkSize = 0;
|
---|
1185 | rsrcFork->forkSize = 0;
|
---|
1186 | otherForks = NULL;
|
---|
1187 | otherForksCount = 0;
|
---|
1188 | iterator.initialize = 0;
|
---|
1189 | done = false;
|
---|
1190 |
|
---|
1191 | // Iterate through the list of forks, processing each fork name in turn.
|
---|
1192 | while (osErr == noErr && ! done)
|
---|
1193 | {
|
---|
1194 | osErr = FSIterateForks(source, &iterator, &thisForkName, &thisForkSize, NULL);
|
---|
1195 | if (osErr == errFSNoMoreItems)
|
---|
1196 | {
|
---|
1197 | osErr = noErr;
|
---|
1198 | done = true;
|
---|
1199 | }
|
---|
1200 | else if (osErr == noErr)
|
---|
1201 | {
|
---|
1202 | if ( CompareHFSUniStr255(&thisForkName, &dataFork->forkName) )
|
---|
1203 | dataFork->forkSize = thisForkSize;
|
---|
1204 | else if ( CompareHFSUniStr255(&thisForkName, &rsrcFork->forkName) )
|
---|
1205 | rsrcFork->forkSize = thisForkSize;
|
---|
1206 | else
|
---|
1207 | {
|
---|
1208 | // We've found a fork other than the resource and data forks.
|
---|
1209 | // We have to add it to the otherForks array. But the array
|
---|
1210 | // a) may not have been created yet, and b) may not contain
|
---|
1211 | // enough elements to hold the new fork.
|
---|
1212 |
|
---|
1213 | if (otherForks == NULL) // The array hasn't been allocated yet, allocate it.
|
---|
1214 | {
|
---|
1215 | otherForksMemoryBlockCount = kExpectedForkCount;
|
---|
1216 | otherForks = ( ForkTracker* ) NewPtr( sizeof(ForkTracker) * kExpectedForkCount );
|
---|
1217 | if (otherForks == NULL)
|
---|
1218 | osErr = memFullErr;
|
---|
1219 | }
|
---|
1220 | else if (otherForksCount == otherForksMemoryBlockCount)
|
---|
1221 | { // If the array doesn't contain enough elements, grow it.
|
---|
1222 | ForkTrackerPtr newOtherForks;
|
---|
1223 |
|
---|
1224 | newOtherForks = (ForkTracker*)NewPtr(sizeof(ForkTracker) * (otherForksCount + kExpectedForkCount));
|
---|
1225 | if( newOtherForks != NULL)
|
---|
1226 | {
|
---|
1227 | BlockMoveData(otherForks, newOtherForks, sizeof(ForkTracker) * otherForksCount);
|
---|
1228 | otherForksMemoryBlockCount += kExpectedForkCount;
|
---|
1229 | DisposePtr((char*)otherForks);
|
---|
1230 | otherForks = newOtherForks;
|
---|
1231 | }
|
---|
1232 | else
|
---|
1233 | osErr = memFullErr;
|
---|
1234 | }
|
---|
1235 |
|
---|
1236 | // If we have no error, we know we have space in the otherForks
|
---|
1237 | // array to place the new fork. Put it there and increment the
|
---|
1238 | // count of forks.
|
---|
1239 |
|
---|
1240 | if (osErr == noErr)
|
---|
1241 | {
|
---|
1242 | BlockMoveData(&thisForkName, &otherForks[otherForksCount].forkName, sizeof(thisForkName));
|
---|
1243 | otherForks[otherForksCount].forkSize = thisForkSize;
|
---|
1244 | otherForks[otherForksCount].forkDestRefNum = 0;
|
---|
1245 | ++otherForksCount;
|
---|
1246 | }
|
---|
1247 | }
|
---|
1248 | }
|
---|
1249 | }
|
---|
1250 |
|
---|
1251 | // Clean up.
|
---|
1252 |
|
---|
1253 | if (osErr != noErr)
|
---|
1254 | {
|
---|
1255 | if (otherForks != NULL)
|
---|
1256 | {
|
---|
1257 | DisposePtr((char*)otherForks);
|
---|
1258 | otherForks = NULL;
|
---|
1259 | }
|
---|
1260 | otherForksCount = 0;
|
---|
1261 | }
|
---|
1262 |
|
---|
1263 | *otherForksParam = otherForks;
|
---|
1264 | *otherForksCountParam = otherForksCount;
|
---|
1265 |
|
---|
1266 | mycheck_noerr( osErr ); // put up debug assert in debug builds
|
---|
1267 |
|
---|
1268 | return osErr;
|
---|
1269 | }
|
---|
1270 |
|
---|
1271 | /*****************************************************************************/
|
---|
1272 |
|
---|
1273 | #pragma mark ----- Calculate Buffer Size -----
|
---|
1274 |
|
---|
1275 | OSErr CalculateBufferSize( const FSRef *source, const FSRef *destDir,
|
---|
1276 | ByteCount * bufferSize )
|
---|
1277 | {
|
---|
1278 | FSVolumeRefNum sourceVRefNum,
|
---|
1279 | destVRefNum;
|
---|
1280 | ByteCount tmpBufferSize = 0;
|
---|
1281 | OSErr osErr = ( source != NULL && destDir != NULL && bufferSize != NULL ) ?
|
---|
1282 | noErr : paramErr;
|
---|
1283 |
|
---|
1284 | if( osErr == noErr )
|
---|
1285 | osErr = FSGetVRefNum( source, &sourceVRefNum );
|
---|
1286 | if( osErr == noErr )
|
---|
1287 | osErr = FSGetVRefNum( destDir, &destVRefNum);
|
---|
1288 | if( osErr == noErr)
|
---|
1289 | {
|
---|
1290 | tmpBufferSize = BufferSizeForThisVolume(sourceVRefNum);
|
---|
1291 | if (destVRefNum != sourceVRefNum)
|
---|
1292 | {
|
---|
1293 | ByteCount tmp = BufferSizeForThisVolume(destVRefNum);
|
---|
1294 | if (tmp < tmpBufferSize)
|
---|
1295 | tmpBufferSize = tmp;
|
---|
1296 | }
|
---|
1297 | }
|
---|
1298 |
|
---|
1299 | *bufferSize = tmpBufferSize;
|
---|
1300 |
|
---|
1301 | mycheck_noerr( osErr ); // put up debug assert in debug builds
|
---|
1302 |
|
---|
1303 | return osErr;
|
---|
1304 | }
|
---|
1305 |
|
---|
1306 | /*****************************************************************************/
|
---|
1307 |
|
---|
1308 | // This routine calculates the appropriate buffer size for
|
---|
1309 | // the given vRefNum. It's a simple composition of FSGetVolParms
|
---|
1310 | // BufferSizeForThisVolumeSpeed.
|
---|
1311 | ByteCount BufferSizeForThisVolume(FSVolumeRefNum vRefNum)
|
---|
1312 | {
|
---|
1313 | GetVolParmsInfoBuffer volParms;
|
---|
1314 | ByteCount volumeBytesPerSecond = 0;
|
---|
1315 | UInt32 actualSize;
|
---|
1316 | OSErr osErr;
|
---|
1317 |
|
---|
1318 | osErr = FSGetVolParms( vRefNum, sizeof(volParms), &volParms, &actualSize );
|
---|
1319 | if( osErr == noErr )
|
---|
1320 | {
|
---|
1321 | // Version 1 of the GetVolParmsInfoBuffer included the vMAttrib
|
---|
1322 | // field, so we don't really need to test actualSize. A noErr
|
---|
1323 | // result indicates that we have the info we need. This is
|
---|
1324 | // just a paranoia check.
|
---|
1325 |
|
---|
1326 | mycheck(actualSize >= offsetof(GetVolParmsInfoBuffer, vMVolumeGrade));
|
---|
1327 |
|
---|
1328 | // On the other hand, vMVolumeGrade was not introduced until
|
---|
1329 | // version 2 of the GetVolParmsInfoBuffer, so we have to explicitly
|
---|
1330 | // test whether we got a useful value.
|
---|
1331 |
|
---|
1332 | if( ( actualSize >= offsetof(GetVolParmsInfoBuffer, vMForeignPrivID) ) &&
|
---|
1333 | ( volParms.vMVolumeGrade <= 0 ) )
|
---|
1334 | {
|
---|
1335 | volumeBytesPerSecond = -volParms.vMVolumeGrade;
|
---|
1336 | }
|
---|
1337 | }
|
---|
1338 |
|
---|
1339 | mycheck_noerr( osErr ); // put up debug assert in debug builds
|
---|
1340 |
|
---|
1341 | return BufferSizeForThisVolumeSpeed(volumeBytesPerSecond);
|
---|
1342 | }
|
---|
1343 |
|
---|
1344 | /*****************************************************************************/
|
---|
1345 |
|
---|
1346 | // Calculate an appropriate copy buffer size based on the volumes
|
---|
1347 | // rated speed. Our target is to use a buffer that takes 0.25
|
---|
1348 | // seconds to fill. This is necessary because the volume might be
|
---|
1349 | // mounted over a very slow link (like ARA), and if we do a 256 KB
|
---|
1350 | // read over an ARA link we'll block the File Manager queue for
|
---|
1351 | // so long that other clients (who might have innocently just
|
---|
1352 | // called PBGetCatInfoSync) will block for a noticeable amount of time.
|
---|
1353 | //
|
---|
1354 | // Note that volumeBytesPerSecond might be 0, in which case we assume
|
---|
1355 | // some default value.
|
---|
1356 | ByteCount BufferSizeForThisVolumeSpeed(UInt32 volumeBytesPerSecond)
|
---|
1357 | {
|
---|
1358 | ByteCount bufferSize;
|
---|
1359 |
|
---|
1360 | if (volumeBytesPerSecond == 0)
|
---|
1361 | bufferSize = kDefaultCopyBufferSize;
|
---|
1362 | else
|
---|
1363 | { // We want to issue a single read that takes 0.25 of a second,
|
---|
1364 | // so devide the bytes per second by 4.
|
---|
1365 | bufferSize = volumeBytesPerSecond / 4;
|
---|
1366 | }
|
---|
1367 |
|
---|
1368 | // Round bufferSize down to 512 byte boundary.
|
---|
1369 | bufferSize &= ~0x01FF;
|
---|
1370 |
|
---|
1371 | // Clip to sensible limits.
|
---|
1372 | if (bufferSize < kMinimumCopyBufferSize)
|
---|
1373 | bufferSize = kMinimumCopyBufferSize;
|
---|
1374 | else if (bufferSize > kMaximumCopyBufferSize)
|
---|
1375 | bufferSize = kMaximumCopyBufferSize;
|
---|
1376 |
|
---|
1377 | return bufferSize;
|
---|
1378 | }
|
---|
1379 |
|
---|
1380 | /*****************************************************************************/
|
---|
1381 |
|
---|
1382 | #pragma mark ----- Delete Objects -----
|
---|
1383 |
|
---|
1384 | OSErr FSDeleteObjects( const FSRef *source )
|
---|
1385 | {
|
---|
1386 | FSCatalogInfo catalogInfo;
|
---|
1387 | OSErr osErr = ( source != NULL ) ? noErr : paramErr;
|
---|
1388 |
|
---|
1389 | // get nodeFlags for container
|
---|
1390 | if( osErr == noErr )
|
---|
1391 | osErr = FSGetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL,NULL);
|
---|
1392 | if( osErr == noErr && (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0 )
|
---|
1393 | { // its a directory, so delete its contents before we delete it
|
---|
1394 | osErr = FSDeleteFolder(source);
|
---|
1395 | }
|
---|
1396 | if( osErr == noErr && (catalogInfo.nodeFlags & kFSNodeLockedMask) != 0 ) // is object locked?
|
---|
1397 | { // then attempt to unlock the object (ignore osErr since FSDeleteObject will set it correctly)
|
---|
1398 | catalogInfo.nodeFlags &= ~kFSNodeLockedMask;
|
---|
1399 | (void) FSSetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo);
|
---|
1400 | }
|
---|
1401 | if( osErr == noErr ) // delete the object (if it was a directory it is now empty, so we can delete it)
|
---|
1402 | osErr = FSDeleteObject(source);
|
---|
1403 |
|
---|
1404 | mycheck_noerr( osErr );
|
---|
1405 |
|
---|
1406 | return ( osErr );
|
---|
1407 | }
|
---|
1408 |
|
---|
1409 | /*****************************************************************************/
|
---|
1410 |
|
---|
1411 | #pragma mark ----- Delete Folders -----
|
---|
1412 |
|
---|
1413 | OSErr FSDeleteFolder( const FSRef *container )
|
---|
1414 | {
|
---|
1415 | FSDeleteObjectGlobals theGlobals;
|
---|
1416 |
|
---|
1417 | theGlobals.result = ( container != NULL ) ? noErr : paramErr;
|
---|
1418 |
|
---|
1419 | // delete container's contents
|
---|
1420 | if( theGlobals.result == noErr )
|
---|
1421 | FSDeleteFolderLevel(container, &theGlobals);
|
---|
1422 |
|
---|
1423 | mycheck_noerr( theGlobals.result );
|
---|
1424 |
|
---|
1425 | return ( theGlobals.result );
|
---|
1426 | }
|
---|
1427 |
|
---|
1428 | /*****************************************************************************/
|
---|
1429 |
|
---|
1430 | void FSDeleteFolderLevel( const FSRef *container,
|
---|
1431 | FSDeleteObjectGlobals *theGlobals)
|
---|
1432 | {
|
---|
1433 | FSIterator iterator;
|
---|
1434 | FSRef itemToDelete;
|
---|
1435 | UInt16 nodeFlags;
|
---|
1436 |
|
---|
1437 | // Open FSIterator for flat access and give delete optimization hint
|
---|
1438 | theGlobals->result = FSOpenIterator(container, kFSIterateFlat + kFSIterateDelete, &iterator);
|
---|
1439 | if ( theGlobals->result == noErr )
|
---|
1440 | {
|
---|
1441 | do // delete the contents of the directory
|
---|
1442 | {
|
---|
1443 | // get 1 item to delete
|
---|
1444 | theGlobals->result = FSGetCatalogInfoBulk( iterator, 1, &theGlobals->actualObjects,
|
---|
1445 | NULL, kFSCatInfoNodeFlags, &theGlobals->catalogInfo,
|
---|
1446 | &itemToDelete, NULL, NULL);
|
---|
1447 | if ( (theGlobals->result == noErr) && (theGlobals->actualObjects == 1) )
|
---|
1448 | {
|
---|
1449 | // save node flags in local in case we have to recurse */
|
---|
1450 | nodeFlags = theGlobals->catalogInfo.nodeFlags;
|
---|
1451 |
|
---|
1452 | // is it a directory?
|
---|
1453 | if ( (nodeFlags & kFSNodeIsDirectoryMask) != 0 )
|
---|
1454 | { // yes -- delete its contents before attempting to delete it */
|
---|
1455 | FSDeleteFolderLevel(&itemToDelete, theGlobals);
|
---|
1456 | }
|
---|
1457 | if ( theGlobals->result == noErr) // are we still OK to delete?
|
---|
1458 | {
|
---|
1459 | if ( (nodeFlags & kFSNodeLockedMask) != 0 ) // is item locked?
|
---|
1460 | { // then attempt to unlock it (ignore result since FSDeleteObject will set it correctly)
|
---|
1461 | theGlobals->catalogInfo.nodeFlags = nodeFlags & ~kFSNodeLockedMask;
|
---|
1462 | (void) FSSetCatalogInfo(&itemToDelete, kFSCatInfoNodeFlags, &theGlobals->catalogInfo);
|
---|
1463 | }
|
---|
1464 | // delete the item
|
---|
1465 | theGlobals->result = FSDeleteObject(&itemToDelete);
|
---|
1466 | }
|
---|
1467 | }
|
---|
1468 | } while ( theGlobals->result == noErr );
|
---|
1469 |
|
---|
1470 | // we found the end of the items normally, so return noErr
|
---|
1471 | if ( theGlobals->result == errFSNoMoreItems )
|
---|
1472 | theGlobals->result = noErr;
|
---|
1473 |
|
---|
1474 | // close the FSIterator (closing an open iterator should never fail)
|
---|
1475 | myverify_noerr(FSCloseIterator(iterator));
|
---|
1476 | }
|
---|
1477 |
|
---|
1478 | mycheck_noerr( theGlobals->result );
|
---|
1479 |
|
---|
1480 | return;
|
---|
1481 | }
|
---|
1482 |
|
---|
1483 | /*****************************************************************************/
|
---|
1484 |
|
---|
1485 | #pragma mark ----- Utilities -----
|
---|
1486 |
|
---|
1487 | // Figures out if the given directory is a drop box or not
|
---|
1488 | // if it is, the Copy Engine will behave slightly differently
|
---|
1489 | OSErr IsDropBox(const FSRef* source, Boolean *isDropBox)
|
---|
1490 | {
|
---|
1491 | FSCatalogInfo tmpCatInfo;
|
---|
1492 | FSSpec sourceSpec;
|
---|
1493 | Boolean isDrop = false;
|
---|
1494 | OSErr osErr;
|
---|
1495 |
|
---|
1496 | // get info about the destination, and an FSSpec to it for PBHGetDirAccess
|
---|
1497 | osErr = FSGetCatalogInfo(source, kFSCatInfoNodeFlags | kFSCatInfoPermissions, &tmpCatInfo, NULL, &sourceSpec, NULL);
|
---|
1498 | if( osErr == noErr ) // make sure the source is a directory
|
---|
1499 | osErr = ((tmpCatInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) ? noErr : errFSNotAFolder;
|
---|
1500 | if( osErr == noErr )
|
---|
1501 | {
|
---|
1502 | HParamBlockRec hPB;
|
---|
1503 |
|
---|
1504 | BlockZero(&hPB, sizeof( HParamBlockRec ));
|
---|
1505 |
|
---|
1506 | hPB.accessParam.ioNamePtr = sourceSpec.name;
|
---|
1507 | hPB.accessParam.ioVRefNum = sourceSpec.vRefNum;
|
---|
1508 | hPB.accessParam.ioDirID = sourceSpec.parID;
|
---|
1509 |
|
---|
1510 | // This is the official way (reads: the way X Finder does it) to figure
|
---|
1511 | // out the current users access privileges to a given directory
|
---|
1512 | osErr = PBHGetDirAccessSync(&hPB);
|
---|
1513 | if( osErr == noErr ) // its a drop folder if the current user only has write access
|
---|
1514 | isDrop = (hPB.accessParam.ioACAccess & kPrivilegesMask) == kioACAccessUserWriteMask;
|
---|
1515 | else if ( osErr == paramErr )
|
---|
1516 | {
|
---|
1517 | // There is a bug (2908703) in the Classic File System (not OS 9.x or Carbon)
|
---|
1518 | // on 10.1.x where PBHGetDirAccessSync sometimes returns paramErr even when the
|
---|
1519 | // data passed in is correct. This is a workaround/hack for that problem,
|
---|
1520 | // but is not as accurate.
|
---|
1521 | // Basically, if "Everyone" has only Write/Search access then its a drop folder
|
---|
1522 | // that is the most common case when its a drop folder
|
---|
1523 | FSPermissionInfo *tmpPerm = (FSPermissionInfo *)tmpCatInfo.permissions;
|
---|
1524 | isDrop = ((tmpPerm->mode & kRWXOtherAccessMask) == kDropFolderValue);
|
---|
1525 | osErr = noErr;
|
---|
1526 | }
|
---|
1527 | }
|
---|
1528 |
|
---|
1529 | *isDropBox = isDrop;
|
---|
1530 |
|
---|
1531 | mycheck_noerr( osErr );
|
---|
1532 |
|
---|
1533 | return osErr;
|
---|
1534 | }
|
---|
1535 |
|
---|
1536 | // The copy engine is going to set each item's creation date
|
---|
1537 | // to kMagicBusyCreationDate while it's copying the item.
|
---|
1538 | // But kMagicBusyCreationDate is an old-style 32-bit date/time,
|
---|
1539 | // while the HFS Plus APIs use the new 64-bit date/time. So
|
---|
1540 | // we have to call a happy UTC utilities routine to convert from
|
---|
1541 | // the local time kMagicBusyCreationDate to a UTCDateTime
|
---|
1542 | // gMagicBusyCreationDate, which the File Manager will store
|
---|
1543 | // on disk and which the Finder we read back using the old
|
---|
1544 | // APIs, whereupon the File Manager will convert it back
|
---|
1545 | // to local time (and hopefully get the kMagicBusyCreationDate
|
---|
1546 | // back!).
|
---|
1547 | OSErr GetMagicBusyCreationDate( UTCDateTime *date )
|
---|
1548 | {
|
---|
1549 | UTCDateTime tmpDate = {0,0,0};
|
---|
1550 | OSErr osErr = ( date != NULL ) ? noErr : paramErr;
|
---|
1551 |
|
---|
1552 | if( osErr == noErr )
|
---|
1553 | osErr = ConvertLocalTimeToUTC(kMagicBusyCreationDate, &tmpDate.lowSeconds);
|
---|
1554 | if( osErr == noErr )
|
---|
1555 | *date = tmpDate;
|
---|
1556 |
|
---|
1557 | mycheck_noerr( osErr ); // put up debug assert in debug builds
|
---|
1558 |
|
---|
1559 | return osErr;
|
---|
1560 | }
|
---|
1561 |
|
---|
1562 | // compares two HFSUniStr255 for equality
|
---|
1563 | // return true if they are identical, false if not
|
---|
1564 | Boolean CompareHFSUniStr255(const HFSUniStr255 *lhs, const HFSUniStr255 *rhs)
|
---|
1565 | {
|
---|
1566 | return (lhs->length == rhs->length)
|
---|
1567 | && (memcmp(lhs->unicode, rhs->unicode, lhs->length * sizeof(UniChar)) == 0);
|
---|
1568 | }
|
---|
1569 |
|
---|
1570 | /*****************************************************************************/
|
---|
1571 |
|
---|
1572 | OSErr FSGetVRefNum(const FSRef *ref, FSVolumeRefNum *vRefNum)
|
---|
1573 | {
|
---|
1574 | FSCatalogInfo catalogInfo;
|
---|
1575 | OSErr osErr = ( ref != NULL && vRefNum != NULL ) ? noErr : paramErr;
|
---|
1576 |
|
---|
1577 | if( osErr == noErr ) /* get the volume refNum from the FSRef */
|
---|
1578 | osErr = FSGetCatalogInfo(ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
|
---|
1579 | if( osErr == noErr )
|
---|
1580 | *vRefNum = catalogInfo.volume;
|
---|
1581 |
|
---|
1582 | mycheck_noerr( osErr );
|
---|
1583 |
|
---|
1584 | return osErr;
|
---|
1585 | }
|
---|
1586 |
|
---|
1587 | /*****************************************************************************/
|
---|
1588 |
|
---|
1589 | OSErr FSGetVolParms( FSVolumeRefNum volRefNum,
|
---|
1590 | UInt32 bufferSize,
|
---|
1591 | GetVolParmsInfoBuffer *volParmsInfo,
|
---|
1592 | UInt32 *actualInfoSize) /* Can Be NULL */
|
---|
1593 | {
|
---|
1594 | HParamBlockRec pb;
|
---|
1595 | OSErr osErr = ( volParmsInfo != NULL ) ? noErr : paramErr;
|
---|
1596 |
|
---|
1597 | if( osErr == noErr )
|
---|
1598 | {
|
---|
1599 | pb.ioParam.ioNamePtr = NULL;
|
---|
1600 | pb.ioParam.ioVRefNum = volRefNum;
|
---|
1601 | pb.ioParam.ioBuffer = (Ptr)volParmsInfo;
|
---|
1602 | pb.ioParam.ioReqCount = (SInt32)bufferSize;
|
---|
1603 | osErr = PBHGetVolParmsSync(&pb);
|
---|
1604 | }
|
---|
1605 | /* return number of bytes the file system returned in volParmsInfo buffer */
|
---|
1606 | if( osErr == noErr && actualInfoSize != NULL)
|
---|
1607 | *actualInfoSize = (UInt32)pb.ioParam.ioActCount;
|
---|
1608 |
|
---|
1609 | mycheck_noerr( osErr ); // put up debug assert in debug builds
|
---|
1610 |
|
---|
1611 | return ( osErr );
|
---|
1612 | }
|
---|
1613 |
|
---|
1614 | /*****************************************************************************/
|
---|
1615 |
|
---|
1616 | OSErr UnicodeNameGetHFSName( UniCharCount nameLength,
|
---|
1617 | const UniChar *name,
|
---|
1618 | TextEncoding textEncodingHint,
|
---|
1619 | Boolean isVolumeName,
|
---|
1620 | Str31 hfsName)
|
---|
1621 | {
|
---|
1622 | UnicodeMapping uMapping;
|
---|
1623 | UnicodeToTextInfo utInfo;
|
---|
1624 | ByteCount unicodeByteLength;
|
---|
1625 | ByteCount unicodeBytesConverted;
|
---|
1626 | ByteCount actualPascalBytes;
|
---|
1627 | OSErr osErr = (hfsName != NULL && name != NULL ) ? noErr : paramErr;
|
---|
1628 |
|
---|
1629 | // make sure output is valid in case we get errors or there's nothing to convert
|
---|
1630 | hfsName[0] = 0;
|
---|
1631 |
|
---|
1632 | unicodeByteLength = nameLength * sizeof(UniChar);
|
---|
1633 | if ( unicodeByteLength == 0 )
|
---|
1634 | osErr = noErr; /* do nothing */
|
---|
1635 | else
|
---|
1636 | {
|
---|
1637 | // if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint
|
---|
1638 | if ( kTextEncodingUnknown == textEncodingHint )
|
---|
1639 | {
|
---|
1640 | ScriptCode script;
|
---|
1641 | RegionCode region;
|
---|
1642 |
|
---|
1643 | script = (ScriptCode)GetScriptManagerVariable(smSysScript);
|
---|
1644 | region = (RegionCode)GetScriptManagerVariable(smRegionCode);
|
---|
1645 | osErr = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare,
|
---|
1646 | region, NULL, &textEncodingHint );
|
---|
1647 | if ( osErr == paramErr )
|
---|
1648 | { // ok, ignore the region and try again
|
---|
1649 | osErr = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare,
|
---|
1650 | kTextRegionDontCare, NULL,
|
---|
1651 | &textEncodingHint );
|
---|
1652 | }
|
---|
1653 | if ( osErr != noErr ) // ok... try something
|
---|
1654 | textEncodingHint = kTextEncodingMacRoman;
|
---|
1655 | }
|
---|
1656 |
|
---|
1657 | uMapping.unicodeEncoding = CreateTextEncoding( kTextEncodingUnicodeV2_0,
|
---|
1658 | kUnicodeCanonicalDecompVariant,
|
---|
1659 | kUnicode16BitFormat);
|
---|
1660 | uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint);
|
---|
1661 | uMapping.mappingVersion = kUnicodeUseHFSPlusMapping;
|
---|
1662 |
|
---|
1663 | osErr = CreateUnicodeToTextInfo(&uMapping, &utInfo);
|
---|
1664 | if( osErr == noErr )
|
---|
1665 | {
|
---|
1666 | osErr = ConvertFromUnicodeToText( utInfo, unicodeByteLength, name, kUnicodeLooseMappingsMask,
|
---|
1667 | 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */
|
---|
1668 | isVolumeName ? kHFSMaxVolumeNameChars : kHFSMaxFileNameChars,
|
---|
1669 | &unicodeBytesConverted, &actualPascalBytes, &hfsName[1]);
|
---|
1670 | }
|
---|
1671 | if( osErr == noErr )
|
---|
1672 | hfsName[0] = actualPascalBytes;
|
---|
1673 |
|
---|
1674 | // verify the result in debug builds -- there's really not anything you can do if it fails
|
---|
1675 | myverify_noerr(DisposeUnicodeToTextInfo(&utInfo));
|
---|
1676 | }
|
---|
1677 |
|
---|
1678 | mycheck_noerr( osErr ); // put up debug assert in debug builds
|
---|
1679 |
|
---|
1680 | return ( osErr );
|
---|
1681 | }
|
---|
1682 |
|
---|
1683 | /*****************************************************************************/
|
---|
1684 |
|
---|
1685 | OSErr FSMakeFSRef( FSVolumeRefNum volRefNum,
|
---|
1686 | SInt32 dirID,
|
---|
1687 | ConstStr255Param name,
|
---|
1688 | FSRef *ref)
|
---|
1689 | {
|
---|
1690 | FSRefParam pb;
|
---|
1691 | OSErr osErr = ( ref != NULL ) ? noErr : paramErr;
|
---|
1692 |
|
---|
1693 | if( osErr == noErr )
|
---|
1694 | {
|
---|
1695 | pb.ioVRefNum = volRefNum;
|
---|
1696 | pb.ioDirID = dirID;
|
---|
1697 | pb.ioNamePtr = (StringPtr)name;
|
---|
1698 | pb.newRef = ref;
|
---|
1699 | osErr = PBMakeFSRefSync(&pb);
|
---|
1700 | }
|
---|
1701 |
|
---|
1702 | mycheck_noerr( osErr ); // put up debug assert in debug builds
|
---|
1703 |
|
---|
1704 | return ( osErr );
|
---|
1705 | }
|
---|