VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/MoreFiles/FSCopyObject.c@ 2318

Last change on this file since 2318 was 589, checked in by vboxsync, 18 years ago

Make it build and run on Mac OS X.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 62.2 KB
Line 
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
62enum { // 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
69enum { // CalculateForksToCopy
70 kExpectedForkCount = 10 // Number of non-classic forks we expect.
71}; // (i.e. non resource/data forks)
72
73enum { // CheckForDestInsideSource
74 errFSDestInsideSource = -1234
75};
76
77enum {
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
113struct CopyParams {
114 UTCDateTime magicBusyCreateDate;
115 void *copyBuffer;
116 ByteCount copyBufferSize;
117 Boolean copyingToDropFolder;
118 Boolean copyingToLocalVolume;
119};
120typedef 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
125struct FilterParams {
126 FSCatalogInfoBitmap whichInfo;
127 CopyObjectFilterProcPtr filterProcPtr;
128 FSSpec fileSpec;
129 FSSpec *fileSpecPtr;
130 HFSUniStr255 fileName;
131 HFSUniStr255 *fileNamePtr;
132 void *yourDataPtr;
133};
134typedef 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...
142struct ForkTracker {
143 HFSUniStr255 forkName;
144 SInt64 forkSize;
145 SInt16 forkDestRefNum;
146};
147typedef struct ForkTracker ForkTracker;
148typedef ForkTracker *ForkTrackerPtr;
149
150 // The FSCopyObjectGlobals data structure holds information needed to do
151 // the recursive copy of a directory.
152struct 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};
166typedef struct FSCopyObjectGlobals FSCopyObjectGlobals;
167
168 // The FSDeleteObjectGlobals data structure holds information needed to
169 // recursively delete a directory
170struct FSDeleteObjectGlobals
171{
172 FSCatalogInfo catalogInfo; /* FSCatalogInfo */
173 ItemCount actualObjects; /* number of objects returned */
174 OSErr result; /* result */
175};
176typedef struct FSDeleteObjectGlobals FSDeleteObjectGlobals;
177
178#pragma mark ----- Local Prototypes -----
179
180static 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
187static 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
194static 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
201static 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
208static 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
216static OSErr FSCopyFolderLevel( FSCopyObjectGlobals *theGlobals, const HFSUniStr255 *destName );
217
218static OSErr CheckForDestInsideSource( const FSRef *source,
219 const FSRef *destDir);
220
221static OSErr CopyItemsForks( const FSRef *source,
222 const FSRef *dest,
223 CopyParams *params);
224
225static OSErr OpenAllForks( const FSRef *dest,
226 const ForkTrackerPtr dataFork,
227 const ForkTrackerPtr rsrcFork,
228 ForkTrackerPtr otherForks,
229 ItemCount otherForksCount);
230
231static OSErr CopyFork( const FSRef *source,
232 const FSRef *dest,
233 const ForkTrackerPtr sourceFork,
234 const CopyParams *params);
235
236static OSErr CloseAllForks( SInt16 dataRefNum,
237 SInt16 rsrcRefNum,
238 ForkTrackerPtr otherForks,
239 ItemCount otherForksCount);
240
241static OSErr CalculateForksToCopy( const FSRef *source,
242 const ForkTrackerPtr dataFork,
243 const ForkTrackerPtr rsrcFork,
244 ForkTrackerPtr *otherForksParam,
245 ItemCount *otherForksCountParam);
246
247static OSErr CalculateBufferSize( const FSRef *source,
248 const FSRef *destDir,
249 ByteCount * bufferSize);
250
251static ByteCount BufferSizeForThisVolume(FSVolumeRefNum vRefNum);
252
253static ByteCount BufferSizeForThisVolumeSpeed(UInt32 volumeBytesPerSecond);
254
255static OSErr IsDropBox( const FSRef* source,
256 Boolean *isDropBox);
257
258static OSErr GetMagicBusyCreationDate( UTCDateTime *date );
259
260static Boolean CompareHFSUniStr255(const HFSUniStr255 *lhs,
261 const HFSUniStr255 *rhs);
262
263static OSErr FSGetVRefNum( const FSRef *ref,
264 FSVolumeRefNum *vRefNum);
265
266static OSErr FSGetVolParms( FSVolumeRefNum volRefNum,
267 UInt32 bufferSize,
268 GetVolParmsInfoBuffer *volParmsInfo,
269 UInt32 *actualInfoSize); /* Can Be NULL */
270
271static OSErr UnicodeNameGetHFSName( UniCharCount nameLength,
272 const UniChar *name,
273 TextEncoding textEncodingHint,
274 Boolean isVolumeName,
275 Str31 hfsName);
276
277static OSErr FSMakeFSRef( FSVolumeRefNum volRefNum,
278 SInt32 dirID,
279 ConstStr255Param name,
280 FSRef *ref);
281
282static OSErr FSDeleteFolder( const FSRef *container );
283
284static 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.
296OSErr 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, &copyParams.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( &copyParams.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, &copyParams.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, &copyParams, &filterParams, maxLevels, newObject );
382 }
383 else // no
384 osErr = FSCopyFile(source, destDir, destNamePtr, &copyParams, &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
400OSErr 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
445OSErr 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
472OSErr 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 //
530OSErr 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
619OSErr 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
661OSErr 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 //
820OSErr 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
863OSErr 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.
935OSErr 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.
988OSErr 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.
1119OSErr 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.
1168OSErr 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
1277OSErr 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.
1313ByteCount 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.
1358ByteCount 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
1386OSErr 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
1415OSErr 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
1432void 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
1491OSErr 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!).
1549OSErr 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
1566Boolean 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
1574OSErr 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
1591OSErr 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
1618OSErr 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
1687OSErr 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}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette