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