aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/mac_updater
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/mac_updater
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/mac_updater')
-rw-r--r--linden/indra/mac_updater/FSCopyObject.c1986
-rw-r--r--linden/indra/mac_updater/FSCopyObject.h285
-rw-r--r--linden/indra/mac_updater/GenLinkedList.c212
-rw-r--r--linden/indra/mac_updater/GenLinkedList.h120
-rw-r--r--linden/indra/mac_updater/MoreFilesX.c2765
-rw-r--r--linden/indra/mac_updater/MoreFilesX.h1848
-rw-r--r--linden/indra/mac_updater/mac_updater.cpp1106
7 files changed, 8322 insertions, 0 deletions
diff --git a/linden/indra/mac_updater/FSCopyObject.c b/linden/indra/mac_updater/FSCopyObject.c
new file mode 100644
index 0000000..b9210d2
--- /dev/null
+++ b/linden/indra/mac_updater/FSCopyObject.c
@@ -0,0 +1,1986 @@
1/*
2 File: FSCopyObject.c
3
4 Contains: A Copy/Delete Files/Folders engine which uses HFS+ API's.
5 This code takes some tricks/techniques from MoreFilesX and
6 MPFileCopy, wraps them all up into an easy to use API, and
7 adds a bunch of features. It will run on Mac OS 9.1 through
8 9.2.x and 10.1.x and up (Classic, Carbon and Mach-O)
9
10 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
11 ("Apple") in consideration of your agreement to the following terms, and your
12 use, installation, modification or redistribution of this Apple software
13 constitutes acceptance of these terms. If you do not agree with these terms,
14 please do not use, install, modify or redistribute this Apple software.
15
16 In consideration of your agreement to abide by the following terms, and subject
17 to these terms, Apple grants you a personal, non-exclusive license, under Apple�s
18 copyrights in this original Apple software (the "Apple Software"), to use,
19 reproduce, modify and redistribute the Apple Software, with or without
20 modifications, in source and/or binary forms; provided that if you redistribute
21 the Apple Software in its entirety and without modifications, you must retain
22 this notice and the following text and disclaimers in all such redistributions of
23 the Apple Software. Neither the name, trademarks, service marks or logos of
24 Apple Computer, Inc. may be used to endorse or promote products derived from the
25 Apple Software without specific prior written permission from Apple. Except as
26 expressly stated in this notice, no other rights or licenses, express or implied,
27 are granted by Apple herein, including but not limited to any patent rights that
28 may be infringed by your derivative works or by other works in which the Apple
29 Software may be incorporated.
30
31 The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
32 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
33 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
34 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
35 COMBINATION WITH YOUR PRODUCTS.
36
37 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
38 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
39 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
41 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
42 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
43 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44
45 Copyright � 2002-2004 Apple Computer, Inc., All Rights Reserved
46*/
47
48#include "FSCopyObject.h"
49#include "GenLinkedList.h"
50#if !TARGET_API_MAC_OSX
51#include <UnicodeConverter.h>
52#endif
53#include <stddef.h>
54#include <string.h>
55
56#pragma mark ----- Tunable Parameters -----
57
58/* The following constants control the behavior of the copy engine. */
59
60enum { /* BufferSizeForVolSpeed */
61/* kDefaultCopyBufferSize = 2L * 1024 * 1024,*/ /* 2MB, Fast but not very responsive. */
62 kDefaultCopyBufferSize = 256L * 1024, /* 256kB, Slower but can still use machine. */
63 kMaximumCopyBufferSize = 2L * 1024 * 1024,
64 kMinimumCopyBufferSize = 1024
65};
66
67enum { /* CheckForDestInsideSrc */
68 errFSDestInsideSource = -1234
69};
70
71enum {
72 /* for use with PBHGetDirAccess in IsDropBox */
73 kPrivilegesMask = kioACAccessUserWriteMask | kioACAccessUserReadMask | kioACAccessUserSearchMask,
74
75 /* for use with FSGetCatalogInfo and FSPermissionInfo->mode */
76 /* from sys/stat.h... note -- sys/stat.h definitions are in octal */
77 /* */
78 /* You can use these values to adjust the users/groups permissions */
79 /* on a file/folder with FSSetCatalogInfo and extracting the */
80 /* kFSCatInfoPermissions field. See code below for examples */
81 kRWXUserAccessMask = 0x01C0,
82 kReadAccessUser = 0x0100,
83 kWriteAccessUser = 0x0080,
84 kExecuteAccessUser = 0x0040,
85
86 kRWXGroupAccessMask = 0x0038,
87 kReadAccessGroup = 0x0020,
88 kWriteAccessGroup = 0x0010,
89 kExecuteAccessGroup = 0x0008,
90
91 kRWXOtherAccessMask = 0x0007,
92 kReadAccessOther = 0x0004,
93 kWriteAccessOther = 0x0002,
94 kExecuteAccessOther = 0x0001,
95
96 kDropFolderValue = kWriteAccessOther | kExecuteAccessOther
97};
98
99#define kNumObjects 80
100
101#define VolHasCopyFile(volParms) (((volParms)->vMAttrib & (1L << bHasCopyFile)) != 0)
102
103#pragma mark ----- Struct Definitions -----
104
105 /* The CopyParams data structure holds the copy buffer used */
106 /* when copying the forks over, as well as special case */
107 /* info on the destination */
108struct CopyParams {
109 void *copyBuffer;
110 ByteCount copyBufferSize;
111 Boolean copyingToDropFolder;
112 Boolean copyingToLocalVolume;
113 Boolean volHasCopyFile;
114 DupeAction dupeAction;
115};
116typedef struct CopyParams CopyParams;
117
118 /* The FilterParams data structure holds the date and info */
119 /* that the caller wants passed into the Filter Proc, as well */
120 /* as the Filter Proc Pointer itself */
121struct FilterParams {
122 FSCatalogInfoBitmap whichInfo;
123 CopyObjectFilterProcPtr filterProcPtr;
124 Boolean containerChanged;
125 Boolean wantSpec;
126 Boolean wantName;
127 void *yourDataPtr;
128};
129typedef struct FilterParams FilterParams;
130
131 /* The ForkTracker data structure holds information about a specific fork, */
132 /* specifically the name and the refnum. We use this to build a list of */
133 /* all the forks before we start copying them. We need to do this because */
134 /* if we're copying into a drop folder, we must open all the forks before */
135 /* we start copying data into any of them. */
136 /* Plus it's a convenient way to keep track of all the forks... */
137struct ForkTracker {
138 HFSUniStr255 forkName;
139 SInt64 forkSize;
140 SInt16 forkDestRefNum;
141};
142typedef struct ForkTracker ForkTracker;
143typedef ForkTracker *ForkTrackerPtr;
144
145 /* The FolderListData data structure holds FSRefs to the source and */
146 /* coorisponding destination folder, as well as which level its on */
147 /* for use in ProcessFolderList. */
148struct FolderListData
149{
150 FSRef sourceDirRef;
151 FSRef destDirRef;
152 UInt32 level;
153};
154typedef struct FolderListData FolderListData;
155
156 /* The FSCopyFolderGlobals data structure holds the information needed to */
157 /* copy a directory */
158struct FSCopyFolderGlobals
159{
160 FSRef *sourceDirRef;
161 FSRef *destDirRef;
162
163 FSCatalogInfo *catInfoList;
164 FSRef *srcRefList;
165 HFSUniStr255 *nameList;
166
167 GenLinkedList folderList;
168 GenIteratorPtr folderListIter;
169
170 CopyParams *copyParams;
171 FilterParams *filterParams;
172 Boolean containerChanged;
173
174 ItemCount maxLevels;
175 ItemCount currentLevel;
176};
177typedef struct FSCopyFolderGlobals FSCopyFolderGlobals;
178
179 /* The FSDeleteObjectGlobals data structure holds information needed to */
180 /* recursively delete a directory */
181struct FSDeleteObjectGlobals
182{
183 FSCatalogInfo catalogInfo; /* FSCatalogInfo */
184 ItemCount actualObjects; /* number of objects returned */
185 OSErr result; /* result */
186};
187typedef struct FSDeleteObjectGlobals FSDeleteObjectGlobals;
188
189#pragma mark ----- Local Prototypes -----
190
191static OSErr FSCopyObjectPreflight ( const FSRef *source,
192 const FSRef *destDir,
193 const DupeAction dupeAction,
194 FSCatalogInfo *sourceCatInfo,
195 CopyParams *copyParams, /* can be NULL */
196 HFSUniStr255 *newObjectName,
197 FSRef *deleteMeRef,
198 Boolean *isReplacing,
199 Boolean *isDirectory );
200
201static OSErr FSCopyFile ( const FSRef *source,
202 const FSRef *destDir,
203 const FSCatalogInfo *sourceCatInfo,
204 const HFSUniStr255 *newFileName,
205 CopyParams *copyParams,
206 FilterParams *filterParams,
207 FSRef *newFileRef, /* can be NULL */
208 FSSpec *newFileSpec ); /* can be NULL */
209
210static OSErr CopyFile ( const FSRef *source,
211 FSCatalogInfo *sourceCatInfo,
212 const FSRef *destDir,
213 const HFSUniStr255 *destName, /* can be NULL */
214 CopyParams *copyParams,
215 FSRef *newRef, /* can be NULL */
216 FSSpec *newSpec ); /* can be NULL */
217
218static OSErr FSUsePBHCopyFile ( const FSRef *srcFileRef,
219 const FSRef *dstDirectoryRef,
220 const HFSUniStr255 *destName, /* can be NULL (no rename during copy) */
221 TextEncoding textEncodingHint,
222 FSRef *newRef, /* can be NULL */
223 FSSpec *newSpec ); /* can be NULL */
224
225static OSErr DoCopyFile ( const FSRef *source,
226 FSCatalogInfo *sourceCatInfo,
227 const FSRef *destDir,
228 const HFSUniStr255 *destName,
229 CopyParams *params,
230 FSRef *newRef, /* can be NULL */
231 FSSpec *newSpec ); /* can be NULL */
232
233static OSErr FSCopyFolder ( const FSRef *source,
234 const FSRef *destDir,
235 const FSCatalogInfo *sourceCatInfo,
236 const HFSUniStr255 *newFoldName,
237 CopyParams *copyParams,
238 FilterParams *filterParams,
239 ItemCount maxLevels,
240 FSRef *outDirRef, /* can be NULL */
241 FSSpec *outDirSpec ); /* can be NULL */
242
243static OSErr ProcessFolderList ( FSCopyFolderGlobals *folderGlobals );
244
245static OSErr CopyFolder ( FSCopyFolderGlobals *folderGlobals );
246
247static OSErr CheckForDestInsideSrc ( const FSRef *source,
248 const FSRef *destDir );
249
250static OSErr CopyForks ( const FSRef *source,
251 const FSRef *dest,
252 CopyParams *params );
253
254static OSErr CopyForksToDisk ( const FSRef *source,
255 const FSRef *dest,
256 CopyParams *params );
257
258static OSErr CopyForksToDropBox ( const FSRef *source,
259 const FSRef *dest,
260 CopyParams *params );
261
262static OSErr OpenAllForks ( const FSRef *dest,
263 GenLinkedList *forkList );
264
265static OSErr WriteFork ( const SInt16 srcRefNum,
266 const SInt16 destRefNum,
267 const CopyParams *params,
268 const SInt64 forkSize );
269
270static UInt32 CalcBufferSizeForVol ( const GetVolParmsInfoBuffer *volParms,
271 UInt32 volParmsSize );
272
273static UInt32 BufferSizeForVolSpeed ( UInt32 volumeBytesPerSecond );
274
275static OSErr FSDeleteFolder ( const FSRef *container );
276
277static void FSDeleteFolderLevel ( const FSRef *container,
278 FSDeleteObjectGlobals *theGlobals );
279
280static OSErr IsDropBox ( const FSRef *source,
281 Boolean *isDropBox );
282
283static OSErr GetMagicBusyCreateDate( UTCDateTime *date );
284
285static OSErr FSGetVRefNum ( const FSRef *ref,
286 FSVolumeRefNum *vRefNum );
287
288static OSErr FSGetVolParms ( FSVolumeRefNum volRefNum,
289 UInt32 bufferSize,
290 GetVolParmsInfoBuffer*volParmsInfo,
291 UInt32 *actualInfoSize ); /* Can Be NULL */
292
293static OSErr UniStrToPStr ( const HFSUniStr255 *uniStr,
294 TextEncoding textEncodingHint,
295 Boolean isVolumeName,
296 Str255 pStr );
297
298static OSErr FSMakeFSRef ( FSVolumeRefNum volRefNum,
299 SInt32 dirID,
300 ConstStr255Param name,
301 FSRef *ref );
302
303static OSErr SetupDestination ( const FSRef *destDir,
304 const DupeAction dupeAction,
305 HFSUniStr255 *sourceName,
306 FSRef *deleteMeRef,
307 Boolean *isReplacing);
308
309static OSErr GetUniqueName ( const FSRef *destDir,
310 HFSUniStr255 *sourceName );
311
312static OSErr GetObjectName ( const FSRef *sourceRef,
313 HFSUniStr255 *sourceName,
314 TextEncoding *sourceEncoding );
315
316static OSErr CreateFolder ( const FSRef *sourceRef,
317 const FSRef *destDirRef,
318 const FSCatalogInfo *catalogInfo,
319 const HFSUniStr255 *folderName,
320 CopyParams *params,
321 FSRef *newFSRefPtr,
322 FSSpec *newFSSpecPtr );
323
324static OSErr DoCreateFolder ( const FSRef *sourceRef,
325 const FSRef *destDirRef,
326 const FSCatalogInfo *catalogInfo,
327 const HFSUniStr255 *folderName,
328 CopyParams *params,
329 FSRef *newFSRefPtr,
330 FSSpec *newFSSpecPtr);
331
332static pascal void MyDisposeDataProc ( void *pData );
333
334static pascal void MyCloseForkProc ( void *pData );
335
336/*****************************************************************************/
337/*****************************************************************************/
338/*****************************************************************************/
339
340#pragma mark ----- Copy Objects -----
341
342 /* This routine acts as the top level of the copy engine. */
343OSErr FSCopyObject( const FSRef *source,
344 const FSRef *destDir,
345 ItemCount maxLevels,
346 FSCatalogInfoBitmap whichInfo,
347 DupeAction dupeAction,
348 const HFSUniStr255 *newObjectName, /* can be NULL */
349 Boolean wantFSSpec,
350 Boolean wantName,
351 CopyObjectFilterProcPtr filterProcPtr, /* can be NULL */
352 void *yourDataPtr, /* can be NULL */
353 FSRef *newObjectRef, /* can be NULL */
354 FSSpec *newObjectSpec) /* can be NULL */
355{
356 CopyParams copyParams;
357 FilterParams filterParams;
358 FSCatalogInfo sourceCatInfo;
359 HFSUniStr255 sourceName,
360 tmpObjectName;
361 FSRef tmpObjectRef,
362 deleteMeRef;
363 Boolean isDirectory = false,
364 isReplacing = false;
365 OSErr err = ( source != NULL && destDir != NULL ) ? noErr : paramErr;
366
367 /* Zero out these two FSRefs in case an error occurs before or */
368 /* inside FSCopyObjectPreflight. Paranoia mainly... */
369 BlockZero( &deleteMeRef, sizeof( FSRef ) );
370 BlockZero( &tmpObjectRef, sizeof( FSRef ) );
371
372 /* setup filterParams */
373 filterParams.whichInfo = whichInfo;
374 filterParams.filterProcPtr = filterProcPtr;
375 filterParams.wantSpec = ( filterProcPtr && wantFSSpec ); /* only get this info if */
376 filterParams.wantName = ( filterProcPtr && wantName ); /* a filterProc is provied */
377 filterParams.yourDataPtr = yourDataPtr;
378
379 /* Get and store away the name of the source object */
380 /* and setup the initial name of the new object */
381 if( err == noErr )
382 err = GetObjectName( source, &sourceName, NULL );
383 if( err == noErr )
384 tmpObjectName = (newObjectName != NULL) ? *newObjectName : sourceName;
385
386 if( err == noErr ) /* preflight/prep the destination and our internal variables */
387 err = FSCopyObjectPreflight( source, destDir, dupeAction, &sourceCatInfo, &copyParams, &tmpObjectName, &deleteMeRef, &isReplacing, &isDirectory );
388
389 /* now that we have some info, lets print it */
390 if( err == noErr )
391 {
392 dwarning(( "%s -- err: %d, maxLevels: %u, whichInfo: %08x,\n", __FUNCTION__, err, (unsigned int)maxLevels, (int)whichInfo ));
393 dwarning(( "\t\t\t\tdupeAction: %s, wantSpec: %s, wantName: %s,\n", ((dupeAction == kDupeActionReplace) ? "replace" : ((dupeAction == kDupeActionRename) ? "rename" : "standard")), (filterParams.wantSpec)?"yes":"no", (filterParams.wantName)?"yes":"no" ));
394 dwarning(( "\t\t\t\tfilterProcPtr: 0x%08x, yourDataPtr: 0x%08x,\n", (unsigned int)filterProcPtr, (unsigned int)yourDataPtr ));
395 dwarning(( "\t\t\t\tnewObjectRef: 0x%08x, newObjectSpec: 0x%08x,\n", (unsigned int)newObjectRef, (unsigned int)newObjectSpec ));
396 dwarning(( "\t\t\t\tcopyBufferSize: %dkB, isDirectory: %s, isLocal: %s,\n", (int)copyParams.copyBufferSize/1024, (isDirectory)?"yes":"no", (copyParams.copyingToLocalVolume)?"yes":"no" ));
397 dwarning(( "\t\t\t\tisDropBox: %s, PBHCopyFileSync supported: %s\n\n", (copyParams.copyingToDropFolder)?"yes":"no", (copyParams.volHasCopyFile)?"yes":"no" ));
398 }
399
400 if( err == noErr ) /* now copy the file/folder... */
401 { /* is it a folder? */
402 if ( isDirectory )
403 { /* yes */
404 err = CheckForDestInsideSrc(source, destDir);
405 if( err == noErr )
406 err = FSCopyFolder( source, destDir, &sourceCatInfo, &tmpObjectName, &copyParams, &filterParams, maxLevels, &tmpObjectRef, newObjectSpec );
407 }
408 else /* no */
409 err = FSCopyFile(source, destDir, &sourceCatInfo, &tmpObjectName, &copyParams, &filterParams, &tmpObjectRef, newObjectSpec);
410 }
411
412 /* if an object existed in the destination with the same name as */
413 /* the source and the caller wants to replace it, we had renamed it */
414 /* to ".DeleteMe" earlier. If no errors, we delete it, else delete */
415 /* the one we just created and rename the origenal back to its */
416 /* origenal name. */
417 /* */
418 /* This is done mainly to cover the case of the source being in the */
419 /* destination directory when kDupeActionReplace is selected */
420 /* (3188701) */
421 if( copyParams.dupeAction == kDupeActionReplace && isReplacing == true )
422 {
423 dwarning(("%s -- Cleaning up, this might take a moment. err : %d\n", __FUNCTION__, err));
424
425 if( err == noErr )
426 err = FSDeleteObjects( &deleteMeRef );
427 else
428 { /* not much we can do if the delete or rename fails, we need to preserve */
429 /* the origenal error code that got us here. */
430 /* */
431 /* If an error occurs before or inside SetupDestination, newFileRef and */
432 /* deleteMeRef will be invalid so the delete and rename will simply fail */
433 /* leaving the source and destination unchanged */
434 myverify_noerr( FSDeleteObjects( &tmpObjectRef ) );
435 myverify_noerr( FSRenameUnicode( &deleteMeRef, sourceName.length, sourceName.unicode, sourceCatInfo.textEncodingHint, NULL ) );
436 }
437 }
438
439 if( err == noErr && newObjectRef != NULL )
440 *newObjectRef = tmpObjectRef;
441
442 /* Clean up for space and safety... Who me? */
443 if( copyParams.copyBuffer != NULL )
444 DisposePtr((char*)copyParams.copyBuffer);
445
446 mycheck_noerr( err );
447
448 return err;
449}
450
451/*****************************************************************************/
452
453 /* Does a little preflighting (as the name suggests) to figure out the optimal */
454 /* buffer size, if its a drop box, on a remote volume etc */
455static OSErr FSCopyObjectPreflight( const FSRef *source,
456 const FSRef *destDir,
457 const DupeAction dupeAction,
458 FSCatalogInfo *sourceCatInfo,
459 CopyParams *copyParams,
460 HFSUniStr255 *newObjectName,
461 FSRef *deleteMeRef,
462 Boolean *isReplacing,
463 Boolean *isDirectory)
464{
465 GetVolParmsInfoBuffer srcVolParms,
466 destVolParms;
467 UInt32 srcVolParmsSize = 0,
468 destVolParmsSize = 0;
469 FSVolumeRefNum srcVRefNum = 0,
470 destVRefNum = 0;
471 OSErr err = ( source != NULL && destDir != NULL &&
472 sourceCatInfo != NULL && copyParams != NULL &&
473 newObjectName != NULL && deleteMeRef != NULL &&
474 isDirectory != NULL ) ? noErr : paramErr;
475
476 BlockZero( copyParams, sizeof( CopyParams ) );
477
478 copyParams->dupeAction = dupeAction;
479
480 if( err == noErr ) /* Get the info we will need later about the source object */
481 err = FSGetCatalogInfo( source, kFSCatInfoSettableInfo, sourceCatInfo, NULL, NULL, NULL );
482 if( err == noErr ) /* get the source's vRefNum */
483 err = FSGetVRefNum( source, &srcVRefNum );
484 if( err == noErr ) /* get the source's volParams */
485 err = FSGetVolParms( srcVRefNum, sizeof(GetVolParmsInfoBuffer), &srcVolParms, &srcVolParmsSize );
486 if( err == noErr ) /* get the destination's vRefNum */
487 err = FSGetVRefNum( destDir, &destVRefNum );
488 if( err == noErr )
489 {
490 /* Calculate the optimal copy buffer size for the src vol */
491 copyParams->copyBufferSize = CalcBufferSizeForVol( &srcVolParms, srcVolParmsSize );
492
493 /* if src and dest on different volumes, get its vol parms */
494 /* and calculate its optimal buffer size */
495 /* else destVolParms = srcVolParms */
496 if( srcVRefNum != destVRefNum )
497 {
498 err = FSGetVolParms( destVRefNum, sizeof(GetVolParmsInfoBuffer), &destVolParms, &destVolParmsSize );
499 if( err == noErr )
500 {
501 ByteCount tmpBufferSize = CalcBufferSizeForVol( &destVolParms, destVolParmsSize );
502 if( tmpBufferSize < copyParams->copyBufferSize )
503 copyParams->copyBufferSize = tmpBufferSize;
504 }
505 }
506 else
507 destVolParms = srcVolParms;
508 }
509 if( err == noErr )
510 err = ((copyParams->copyBuffer = NewPtr( copyParams->copyBufferSize )) != NULL ) ? noErr : MemError();
511
512 /* figure out if source is a file or folder */
513 /* if it is on a local volume, */
514 /* if destination is a drop box */
515 /* if source and dest are on same server */
516 /* and if it supports PBHCopyFile */
517 if( err == noErr ) /* is the destination a Drop Box */
518 err = IsDropBox( destDir, &copyParams->copyingToDropFolder );
519 if( err == noErr )
520 {
521 /* Is it a directory */
522 *isDirectory = ((sourceCatInfo->nodeFlags & kFSNodeIsDirectoryMask) != 0);
523 /* destVolParms.vMServerAdr is non-zero for remote volumes */
524 copyParams->copyingToLocalVolume = (destVolParms.vMServerAdr == 0);
525 if( !copyParams->copyingToLocalVolume )
526 {
527 /* If the destination is on a remote volume, and source and dest are on */
528 /* the same server, then it might support PBHCopyFileSync */
529 /* If not, then PBHCopyFileSync won't work */
530
531 /* figure out if the volumes support PBHCopyFileSync */
532 copyParams->volHasCopyFile = ( err == noErr && destVolParms.vMServerAdr == srcVolParms.vMServerAdr ) ?
533 VolHasCopyFile(&srcVolParms) : false;
534 }
535 }
536
537 if( err == noErr )
538 err = SetupDestination( destDir, copyParams->dupeAction, newObjectName, deleteMeRef, isReplacing );
539
540 return err;
541}
542
543#pragma mark ----- Copy Files -----
544
545/*****************************************************************************/
546
547static OSErr FSCopyFile( const FSRef *source,
548 const FSRef *destDir,
549 const FSCatalogInfo *sourceCatInfo,
550 const HFSUniStr255 *newFileName,
551 CopyParams *copyParams,
552 FilterParams *filterParams,
553 FSRef *outFileRef,
554 FSSpec *outFileSpec )
555{
556 FSCatalogInfo catInfo = *sourceCatInfo;
557 FSRef newFileRef;
558 FSSpec newFileSpec;
559 OSErr err = ( source != NULL && destDir != NULL &&
560 copyParams != NULL && filterParams != NULL ) ? noErr : paramErr;
561
562 /* If you would like a Pre-Copy filter (i.e to weed out objects */
563 /* you don't want to copy) you should add it here */
564
565 if( err == noErr ) /* copy the file over */
566 err = CopyFile( source, &catInfo, destDir, newFileName, copyParams, &newFileRef, (filterParams->wantSpec || outFileSpec) ? &newFileSpec : NULL );
567
568 /* Call the IterateFilterProc _after_ the new file was created even if an error occured. */
569 /* Note: if an error occured above, the FSRef and other info might not be valid */
570 if( filterParams->filterProcPtr != NULL )
571 {
572 /* get the extra info the user wanted on the new file that we don't have */
573 if( err == noErr && (filterParams->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone )
574 err = FSGetCatalogInfo( &newFileRef, filterParams->whichInfo & ~kFSCatInfoSettableInfo, &catInfo, NULL, NULL, NULL );
575
576 err = CallCopyObjectFilterProc( filterParams->filterProcPtr, false, 0, err, &catInfo, &newFileRef,
577 (filterParams->wantSpec) ? &newFileSpec : NULL,
578 (filterParams->wantName) ? newFileName : NULL,
579 filterParams->yourDataPtr);
580 }
581
582 if( err == noErr )
583 {
584 if( outFileRef != NULL )
585 *outFileRef = newFileRef;
586 if( outFileSpec != NULL )
587 *outFileSpec = newFileSpec;
588 }
589
590 mycheck_noerr(err);
591
592 return err;
593}
594
595/*****************************************************************************/
596
597static OSErr CopyFile( const FSRef *source,
598 FSCatalogInfo *sourceCatInfo,
599 const FSRef *destDir,
600 const HFSUniStr255 *destName, /* can be NULL */
601 CopyParams *params,
602 FSRef *newFile, /* can be NULL */
603 FSSpec *newSpec ) /* can be NULL */
604{
605 OSErr err = paramErr;
606
607 /* Clear the "inited" bit so that the Finder positions the icon for us. */
608 ((FInfo *)(sourceCatInfo->finderInfo))->fdFlags &= ~kHasBeenInited;
609
610 /* if the volumes support PBHCopyFileSync, try to use it */
611 if( params->volHasCopyFile == true )
612 err = FSUsePBHCopyFile( source, destDir, destName, kTextEncodingUnknown, newFile, newSpec );
613
614 /* if PBHCopyFile didn't work or not supported, */
615 if( err != noErr ) /* then try old school file transfer */
616 err = DoCopyFile( source, sourceCatInfo, destDir, destName, params, newFile, newSpec );
617
618 mycheck_noerr(err);
619
620 return err;
621}
622
623/*****************************************************************************/
624
625 /* Wrapper function for PBHCopyFileSync */
626static OSErr FSUsePBHCopyFile( const FSRef *srcFileRef,
627 const FSRef *dstDirectoryRef,
628 const HFSUniStr255 *destName, /* can be NULL */
629 TextEncoding textEncodingHint,
630 FSRef *newRef, /* can be NULL */
631 FSSpec *newSpec) /* can be NULL */
632{
633 FSSpec srcFileSpec;
634 FSCatalogInfo catalogInfo;
635 HParamBlockRec pb;
636 Str255 hfsName;
637 OSErr err = ( srcFileRef != NULL && dstDirectoryRef != NULL ) ? noErr : paramErr;
638
639 if( err == noErr ) /* get FSSpec of source FSRef */
640 err = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL);
641 if( err == noErr ) /* get the destination vRefNum and nodeID (nodeID is the dirID) */
642 err = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume | kFSCatInfoNodeID, &catalogInfo, NULL, NULL, NULL);
643 if( err == noErr ) /* gather all the info needed */
644 {
645 pb.copyParam.ioVRefNum = srcFileSpec.vRefNum;
646 pb.copyParam.ioDirID = srcFileSpec.parID;
647 pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name;
648 pb.copyParam.ioDstVRefNum = catalogInfo.volume;
649 pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID;
650 pb.copyParam.ioNewName = NULL;
651 if( destName != NULL )
652 err = UniStrToPStr( destName, textEncodingHint, false, hfsName );
653 pb.copyParam.ioCopyName = ( destName != NULL && err == noErr ) ? hfsName : NULL;
654 }
655 if( err == noErr ) /* tell the server to copy the object */
656 err = PBHCopyFileSync(&pb);
657
658 if( err == noErr )
659 {
660 if( newSpec != NULL ) /* caller wants an FSSpec, so make it */
661 myverify_noerr(FSMakeFSSpec( pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, pb.copyParam.ioCopyName, newSpec));
662 if( newRef != NULL ) /* caller wants an FSRef, so make it */
663 myverify_noerr(FSMakeFSRef( pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, pb.copyParam.ioCopyName, newRef));
664 }
665
666 if( err != paramErr ) /* returning paramErr is ok, it means PBHCopyFileSync was not supported */
667 mycheck_noerr(err);
668
669 return err;
670}
671
672/*****************************************************************************/
673
674 /* Copies a file referenced by source to the directory referenced by */
675 /* destDir. destName is the name the file we are going to copy to the */
676 /* destination. sourceCatInfo is the catalog info of the file, which */
677 /* is passed in as an optimization (we could get it by doing a */
678 /* FSGetCatalogInfo but the caller has already done that so we might as */
679 /* well take advantage of that). */
680 /* */
681static OSErr DoCopyFile(const FSRef *source,
682 FSCatalogInfo *sourceCatInfo,
683 const FSRef *destDir,
684 const HFSUniStr255 *destName,
685 CopyParams *params,
686 FSRef *newRef,
687 FSSpec *newSpec )
688{
689 FSRef dest;
690 FSSpec tmpSpec;
691 FSPermissionInfo originalPermissions;
692 OSType originalFileType = 'xxxx';
693 UInt16 originalNodeFlags = kFSCatInfoNone;
694 Boolean getSpec;
695 OSErr err = noErr;
696
697 /* If we're copying to a drop folder, we won't be able to reset this */
698 /* information once the copy is done, so we don't mess it up in */
699 /* the first place. We still clear the locked bit though; items dropped */
700 /* into a drop folder always become unlocked. */
701 if (!params->copyingToDropFolder)
702 {
703 /* Remember to clear the file's type, so the Finder doesn't */
704 /* look at the file until we're done. */
705 originalFileType = ((FInfo *) &sourceCatInfo->finderInfo)->fdType;
706 ((FInfo *) &sourceCatInfo->finderInfo)->fdType = kFirstMagicBusyFiletype;
707
708 /* Remember and clear the file's locked status, so that we can */
709 /* actually write the forks we're about to create. */
710 originalNodeFlags = sourceCatInfo->nodeFlags;
711 }
712 sourceCatInfo->nodeFlags &= ~kFSNodeLockedMask;
713
714 /* figure out if we should get the FSSpec to the new file or not */
715 /* If the caller asked for it, or if we need it for symlinks */
716 getSpec = ( ( newSpec != NULL ) || ( !params->copyingToDropFolder && originalFileType == 'slnk' && ((FInfo *) &sourceCatInfo->finderInfo)->fdCreator == 'rhap' ) );
717
718 /* we need to have user level read/write/execute access to the file we are */
719 /* going to create otherwise FSCreateFileUnicode will return */
720 /* -5000 (afpAccessDenied), and the FSRef returned will be invalid, yet */
721 /* the file is created (size 0k)... bug? */
722 originalPermissions = *((FSPermissionInfo*)sourceCatInfo->permissions);
723 ((FSPermissionInfo*)sourceCatInfo->permissions)->mode |= kRWXUserAccessMask;
724
725 /* Classic only supports 9.1 and higher, so we don't have to worry */
726 /* about 2397324 */
727 if( err == noErr )
728 err = FSCreateFileUnicode(destDir, destName->length, destName->unicode, kFSCatInfoSettableInfo, sourceCatInfo, &dest, ( getSpec ) ? &tmpSpec : NULL );
729 if( err == noErr ) /* Copy the forks over to the new file */
730 err = CopyForks(source, &dest, params);
731
732 /* Restore the original file type, creation and modification dates, */
733 /* locked status and permissions. */
734 /* This is one of the places where we need to handle drop */
735 /* folders as a special case because this FSSetCatalogInfo will fail for */
736 /* an item in a drop folder, so we don't even attempt it. */
737 if (err == noErr && !params->copyingToDropFolder)
738 {
739 ((FInfo *) &sourceCatInfo->finderInfo)->fdType = originalFileType;
740 sourceCatInfo->nodeFlags = originalNodeFlags;
741 *((FSPermissionInfo*)sourceCatInfo->permissions) = originalPermissions;
742
743 /* 2796751, FSSetCatalogInfo returns -36 when setting the Finder Info */
744 /* for a symlink. To workaround this, when the file is a */
745 /* symlink (slnk/rhap) we will finish the copy in two steps. First */
746 /* setting everything but the Finder Info on the file, then calling */
747 /* FSpSetFInfo to set the Finder Info for the file. I would rather use */
748 /* an FSRef function to set the Finder Info, but FSSetCatalogInfo is */
749 /* the only one... catch-22... */
750 /* */
751 /* The Carbon File Manager always sets the type/creator of a symlink to */
752 /* slnk/rhap if the file is a symlink we do the two step, if it isn't */
753 /* we use FSSetCatalogInfo to do all the work. */
754 if ((originalFileType == 'slnk') && (((FInfo *) &sourceCatInfo->finderInfo)->fdCreator == 'rhap'))
755 { /* Its a symlink */
756 /* set all the info, except the Finder info */
757 err = FSSetCatalogInfo(&dest, kFSCatInfoNodeFlags | kFSCatInfoPermissions, sourceCatInfo);
758 if ( err == noErr ) /* set the Finder Info to that file */
759 err = FSpSetFInfo( &tmpSpec, ((FInfo *) &sourceCatInfo->finderInfo) );
760 }
761 else /* its a regular file */
762 err = FSSetCatalogInfo(&dest, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo | kFSCatInfoPermissions, sourceCatInfo);
763 }
764
765 /* If we created the file and the copy failed, try to clean up by */
766 /* deleting the file we created. We do this because, while it's */
767 /* possible for the copy to fail halfway through and the File Manager */
768 /* doesn't really clean up that well in that case, we *really* don't want */
769 /* any half-created files being left around. */
770 /* if the file already existed, we don't want to delete it */
771 if( err == noErr || err == dupFNErr )
772 { /* if everything was fine, then return the new file Spec/Ref */
773 if( newRef != NULL )
774 *newRef = dest;
775 if( newSpec != NULL )
776 *newSpec = tmpSpec;
777 }
778 else
779 myverify_noerr( FSDeleteObjects(&dest) );
780
781 mycheck_noerr(err);
782
783 return err;
784}
785
786/*****************************************************************************/
787
788#pragma mark ----- Copy Folders -----
789
790static OSErr FSCopyFolder( const FSRef *source,
791 const FSRef *destDir,
792 const FSCatalogInfo *sourceCatInfo,
793 const HFSUniStr255 *newObjectName,
794 CopyParams *copyParams,
795 FilterParams *filterParams,
796 ItemCount maxLevels,
797 FSRef *outDirRef,
798 FSSpec *outDirSpec )
799{
800 FSCopyFolderGlobals folderGlobals;
801 FolderListData *tmpListData = NULL;
802 FSCatalogInfo catInfo = *sourceCatInfo;
803 FSRef newDirRef;
804 FSSpec newDirSpec;
805 OSErr err;
806
807 /* setup folder globals */
808 folderGlobals.catInfoList = (FSCatalogInfo*) NewPtr( sizeof( FSCatalogInfo ) * kNumObjects );
809 folderGlobals.srcRefList = (FSRef*) NewPtr( sizeof( FSRef ) * kNumObjects );
810 folderGlobals.nameList = (HFSUniStr255*) NewPtr( sizeof( HFSUniStr255 ) * kNumObjects );
811 folderGlobals.folderListIter = NULL;
812 folderGlobals.copyParams = copyParams;
813 folderGlobals.filterParams = filterParams;
814 folderGlobals.maxLevels = maxLevels;
815 folderGlobals.currentLevel = 0;
816
817 /* if any of the NewPtr calls failed, we MUST bail */
818 err = ( folderGlobals.catInfoList != NULL &&
819 folderGlobals.srcRefList != NULL &&
820 folderGlobals.nameList != NULL ) ? noErr : memFullErr;
821
822 /* init the linked list we will use to keep track of the folders */
823 InitLinkedList( &folderGlobals.folderList, MyDisposeDataProc );
824
825 if( err == noErr && !copyParams->copyingToDropFolder )
826 err = GetMagicBusyCreateDate( &catInfo.createDate );
827 if( err == noErr ) /* create the directory */
828 err = DoCreateFolder( source, destDir, &catInfo, newObjectName, folderGlobals.copyParams, &newDirRef, (filterParams->wantSpec || outDirSpec ) ? &newDirSpec : NULL );
829
830 /* Note: if an error occured above, the FSRef and other info might not be valid */
831 if( filterParams->filterProcPtr != NULL )
832 {
833 /* get the info the user wanted about the source directory we don't have */
834 if( err == noErr && (filterParams->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone )
835 err = FSGetCatalogInfo(&newDirRef, filterParams->whichInfo & ~kFSCatInfoSettableInfo, &catInfo, NULL, NULL, NULL);
836
837 err = CallCopyObjectFilterProc(filterParams->filterProcPtr, false, folderGlobals.currentLevel,
838 err, &catInfo, &newDirRef,
839 ( filterParams->wantSpec ) ? &newDirSpec : NULL,
840 ( filterParams->wantName ) ? newObjectName : NULL,
841 filterParams->yourDataPtr);
842 }
843 if( err == noErr ) /* create the memory for this folder */
844 err = ( ( tmpListData = (FolderListData*) NewPtr( sizeof( FolderListData ) ) ) != NULL ) ? noErr : MemError();
845 if( err == noErr )
846 { /* setup the folder info */
847 tmpListData->sourceDirRef = *source;
848 tmpListData->destDirRef = newDirRef;
849 tmpListData->level = folderGlobals.currentLevel;
850 /* add this folder to the list to give ProcessFolderList something to chew on */
851 err = AddToTail( &folderGlobals.folderList, tmpListData );
852 if( err == noErr ) /* tmpListData added successfully */
853 err = ProcessFolderList( &folderGlobals );
854 else /* error occured, so dispose of memory */
855 DisposePtr( (char*) tmpListData );
856 }
857
858 dwarning(("\n%s -- %u folders were found\n", __FUNCTION__, (unsigned int)GetNumberOfItems( &folderGlobals.folderList ) ));
859
860 /* when we're done destroy the list and free up any memory we allocated */
861 DestroyList( &folderGlobals.folderList );
862
863 /* now that the copy is complete, we can set things back to normal */
864 /* for the directory we just created. */
865 /* We have to do this only for the top directory of the copy */
866 /* all subdirectories were created all at once */
867 if( err == noErr && !folderGlobals.copyParams->copyingToDropFolder )
868 err = FSSetCatalogInfo( &newDirRef, kFSCatInfoCreateDate | kFSCatInfoPermissions, sourceCatInfo );
869
870 /* Copy went as planned, and caller wants an FSRef/FSSpec to the new directory */
871 if( err == noErr )
872 {
873 if( outDirRef != NULL)
874 *outDirRef = newDirRef;
875 if( outDirSpec != NULL )
876 *outDirSpec = newDirSpec;
877 }
878
879 /* clean up for space and safety, who me? */
880 if( folderGlobals.catInfoList )
881 DisposePtr( (char*) folderGlobals.catInfoList );
882 if( folderGlobals.srcRefList )
883 DisposePtr( (char*) folderGlobals.srcRefList );
884 if( folderGlobals.nameList )
885 DisposePtr( (char*) folderGlobals.nameList );
886
887 mycheck_noerr(err);
888
889 return ( err );
890}
891
892/*****************************************************************************/
893
894 /* We now store a list of all the folders/subfolders we encounter in the source */
895 /* Each node in the list contains an FSRef to the source, an FSRef to the */
896 /* mirror folder in the destination, and the level in the source that folder */
897 /* is on. This is done so that we can use FSGetCatalogInfoBulk to its full */
898 /* potential (getting items in bulk). We copy the source one folder at a time. */
899 /* Copying over the contents of each folder before we continue on to the next */
900 /* folder in the list. This allows us to use the File Manager's own caching */
901 /* system to our advantage. */
902static OSErr ProcessFolderList( FSCopyFolderGlobals *folderGlobals )
903{
904 FolderListData *folderListData;
905 OSErr err = noErr;
906
907 /* iterate through the list of folders and copy over each one individually */
908 for( InitIterator( &folderGlobals->folderList, &folderGlobals->folderListIter ); folderGlobals->folderListIter != NULL && err == noErr; Next( &folderGlobals->folderListIter ) )
909 {
910 /* Get the data for this folder */
911 folderListData = (FolderListData*) GetData( folderGlobals->folderListIter );
912 if( folderListData != NULL )
913 {
914 #if DEBUG && !TARGET_API_MAC_OS8
915 {
916 char path[1024];
917 myverify_noerr(FSRefMakePath( &(folderListData->sourceDirRef), (unsigned char*)path, 1024 ));
918 dwarning(("\n\n%s -- Copying contents of\n\t%s\n", __FUNCTION__, path));
919 myverify_noerr(FSRefMakePath( &(folderListData->destDirRef), (unsigned char*)path, 1024 ));
920 dwarning(("\t\tto\n\t%s\n", path));
921 }
922 #endif
923
924 /* stuff the data into our globals */
925 folderGlobals->sourceDirRef = &(folderListData->sourceDirRef);
926 folderGlobals->destDirRef = &(folderListData->destDirRef);
927 folderGlobals->currentLevel = folderListData->level;
928
929 /* Copy over this folder and add any subfolders to our list of folders */
930 /* so they will get processed later */
931 err = CopyFolder( folderGlobals );
932 }
933 }
934
935 return err;
936}
937
938/*****************************************************************************/
939
940 /* Copy the contents of the source into the destination. If any subfolders */
941 /* are found, add them to a local list of folders during the loop stage */
942 /* Once the copy is done, insert the local list into the global list right */
943 /* after the current position in the list. This is done so we don't jump */
944 /* all over the disk getting the different folders to copy */
945static OSErr CopyFolder( FSCopyFolderGlobals *folderGlobals )
946{
947 GenLinkedList tmpList;
948 FolderListData *tmpListData = NULL;
949 FilterParams *filterPtr = folderGlobals->filterParams;
950 FSIterator iterator;
951 FSRef newRef;
952 FSSpec newSpec;
953 UInt32 actualObjects;
954 OSErr err,
955 junkErr;
956 int i;
957
958 /* Init the local list */
959 InitLinkedList( &tmpList, MyDisposeDataProc);
960
961 err = FSOpenIterator( folderGlobals->sourceDirRef, kFSIterateFlat, &iterator );
962 if( err == noErr )
963 {
964 do
965 {
966 /* grab a bunch of objects (kNumObjects) from this folder and copy them over */
967 err = FSGetCatalogInfoBulk( iterator, kNumObjects, &actualObjects, &filterPtr->containerChanged,
968 kFSCatInfoSettableInfo, folderGlobals->catInfoList, folderGlobals->srcRefList,
969 NULL, folderGlobals->nameList );
970 if( ( err == noErr || err == errFSNoMoreItems ) &&
971 ( actualObjects != 0 ) )
972 {
973 dwarning(("%s -- actualObjects retrieved from FSGetCatalogInfoBulk: %u\n",__FUNCTION__, (unsigned int)actualObjects ));
974
975 /* iterate over the objects actually returned */
976 for( i = 0; i < actualObjects; i++ )
977 {
978 /* Any errors in here will be passed to the filter proc */
979 /* we don't want an error in here to prematurely cancel the copy */
980
981 /* If you would like a Pre-Copy filter (i.e to weed out objects */
982 /* you don't want to copy) you should add it here */
983
984 /* Is the new object a directory? */
985 if( ( folderGlobals->catInfoList[i].nodeFlags & kFSNodeIsDirectoryMask ) != 0 )
986 { /* yes */
987 junkErr = CreateFolder( &folderGlobals->srcRefList[i], folderGlobals->destDirRef,
988 &folderGlobals->catInfoList[i], &folderGlobals->nameList[i],
989 folderGlobals->copyParams, &newRef, (filterPtr->wantSpec) ? &newSpec : NULL );
990 /* If maxLevels is zero, we aren't checking levels */
991 /* If currentLevel+1 < maxLevels, add this folder to the list */
992 if( folderGlobals->maxLevels == 0 || (folderGlobals->currentLevel + 1) < folderGlobals->maxLevels )
993 {
994 if( junkErr == noErr ) /* Create memory for folder list data */
995 junkErr = ( ( tmpListData = (FolderListData*) NewPtr( sizeof( FolderListData ) ) ) != NULL ) ? noErr : MemError();
996 if( junkErr == noErr )
997 { /* Setup the folder list data */
998 tmpListData->sourceDirRef = folderGlobals->srcRefList[i];
999 tmpListData->destDirRef = newRef;
1000 tmpListData->level = folderGlobals->currentLevel + 1;
1001
1002 /* Add it to the local list */
1003 junkErr = AddToTail( &tmpList, tmpListData );
1004 }
1005 /* If an error occured and memory was created, we need to dispose of it */
1006 /* since it was not added to the list */
1007 if( junkErr != noErr && tmpListData != NULL )
1008 DisposePtr( (char*) tmpListData );
1009 }
1010 }
1011 else
1012 { /* no */
1013 junkErr = CopyFile( &folderGlobals->srcRefList[i], &folderGlobals->catInfoList[i],
1014 folderGlobals->destDirRef, &folderGlobals->nameList[i],
1015 folderGlobals->copyParams, &newRef, ( filterPtr->wantSpec ) ? &newSpec : NULL );
1016 }
1017
1018 /* Note: if an error occured above, the FSRef and other info might not be valid */
1019 if( filterPtr->filterProcPtr != NULL )
1020 {
1021 if( junkErr == noErr && (filterPtr->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone ) /* get the extra info about the new object that the user wanted that we don't already have */
1022 junkErr = FSGetCatalogInfo( &newRef, filterPtr->whichInfo & ~kFSCatInfoSettableInfo, &folderGlobals->catInfoList[i], NULL, NULL, NULL );
1023
1024 err = CallCopyObjectFilterProc( filterPtr->filterProcPtr, filterPtr->containerChanged,
1025 folderGlobals->currentLevel, junkErr,
1026 &folderGlobals->catInfoList[i], &newRef,
1027 ( filterPtr->wantSpec ) ? &newSpec : NULL,
1028 ( filterPtr->wantName ) ? &folderGlobals->nameList[i] : NULL,
1029 filterPtr->yourDataPtr);
1030 }
1031 }
1032 }
1033 }while( err == noErr );
1034
1035 /* errFSNoMoreItems is OK - it only means we hit the end of this level */
1036 /* afpAccessDenied is OK too - it only means we cannot see inside the directory */
1037 if( err == errFSNoMoreItems || err == afpAccessDenied )
1038 err = noErr;
1039
1040 /* Insert the local list of folders from the current folder into our global list. Even */
1041 /* if no items were added to the local list (due to error, or empty folder), InsertList */
1042 /* handles it correctly. We add the local list even if an error occurred. It will get */
1043 /* disposed of when the global list is destroyed. Doesn't hurt to have a couple extra */
1044 /* steps when we're going to bail anyways. */
1045 InsertList( &folderGlobals->folderList, &tmpList, folderGlobals->folderListIter );
1046
1047 /* Close the FSIterator (closing an open iterator should never fail) */
1048 (void) FSCloseIterator(iterator);
1049 }
1050
1051 mycheck_noerr( err );
1052
1053 return err;
1054}
1055
1056/*****************************************************************************/
1057
1058 /* Determines whether the destination directory is equal to the source */
1059 /* item, or whether it's nested inside the source item. Returns a */
1060 /* errFSDestInsideSource if that's the case. We do this to prevent */
1061 /* endless recursion while copying. */
1062 /* */
1063static OSErr CheckForDestInsideSrc( const FSRef *source,
1064 const FSRef *destDir)
1065{
1066 FSRef thisDir = *destDir;
1067 FSCatalogInfo thisDirInfo;
1068 Boolean done = false;
1069 OSErr err;
1070
1071 do
1072 {
1073 err = FSCompareFSRefs(source, &thisDir);
1074 if (err == noErr)
1075 err = errFSDestInsideSource;
1076 else if (err == diffVolErr)
1077 {
1078 err = noErr;
1079 done = true;
1080 }
1081 else if (err == errFSRefsDifferent)
1082 {
1083 /* This is somewhat tricky. We can ask for the parent of thisDir */
1084 /* by setting the parentRef parameter to FSGetCatalogInfo but, if */
1085 /* thisDir is the volume's FSRef, this will give us back junk. */
1086 /* So we also ask for the parent's dir ID to be returned in the */
1087 /* FSCatalogInfo record, and then check that against the node */
1088 /* ID of the root's parent (ie 1). If we match that, we've made */
1089 /* it to the top of the hierarchy without hitting source, so */
1090 /* we leave with no error. */
1091
1092 err = FSGetCatalogInfo(&thisDir, kFSCatInfoParentDirID, &thisDirInfo, NULL, NULL, &thisDir);
1093 if( ( err == noErr ) && ( thisDirInfo.parentDirID == fsRtParID ) )
1094 done = true;
1095 }
1096 } while ( err == noErr && ! done );
1097
1098 mycheck_noerr( err );
1099
1100 return err;
1101}
1102
1103/*****************************************************************************/
1104
1105#pragma mark ----- Copy Forks -----
1106
1107 /* This is where the majority of the work is done. I special cased */
1108 /* DropBoxes in order to use FSIterateForks to its full potential for */
1109 /* the more common case (read/write permissions). It also simplifies */
1110 /* the code to have it seperate. */
1111static OSErr CopyForks( const FSRef *source,
1112 const FSRef *dest,
1113 CopyParams *params)
1114{
1115 OSErr err;
1116
1117 err = ( !params->copyingToDropFolder ) ? CopyForksToDisk ( source, dest, params ) :
1118 CopyForksToDropBox ( source, dest, params );
1119
1120 mycheck_noerr( err );
1121
1122 return err;
1123}
1124
1125 /* Open each fork individually and copy them over to the destination */
1126static OSErr CopyForksToDisk( const FSRef *source,
1127 const FSRef *dest,
1128 CopyParams *params )
1129{
1130 HFSUniStr255 forkName;
1131 CatPositionRec iterator;
1132 SInt64 forkSize;
1133 SInt16 srcRefNum,
1134 destRefNum;
1135 OSErr err;
1136
1137 /* need to initialize the iterator before using it */
1138 iterator.initialize = 0;
1139
1140 do
1141 {
1142 err = FSIterateForks( source, &iterator, &forkName, &forkSize, NULL );
1143
1144 /* Create the fork. Note: Data and Resource forks are automatically */
1145 /* created when the file is created. FSCreateFork returns noErr for them */
1146 /* We also want to create the fork even if there is no data to preserve */
1147 /* empty forks */
1148 if( err == noErr )
1149 err = FSCreateFork( dest, forkName.length, forkName.unicode );
1150
1151 /* Mac OS 9.0 has a bug (in the AppleShare external file system, */
1152 /* I think) [2410374] that causes FSCreateFork to return an errFSForkExists */
1153 /* error even though the fork is empty. The following code swallows */
1154 /* the error (which is harmless) in that case. */
1155 if( err == errFSForkExists && !params->copyingToLocalVolume )
1156 err = noErr;
1157
1158 /* The remainder of this code only applies if there is actual data */
1159 /* in the source fork. */
1160
1161 if( err == noErr && forkSize > 0 )
1162 {
1163 destRefNum = srcRefNum = 0;
1164
1165 /* Open the destination fork */
1166 err = FSOpenFork(dest, forkName.length, forkName.unicode, fsWrPerm, &destRefNum);
1167 if( err == noErr ) /* Open the source fork */
1168 err = FSOpenFork(source, forkName.length, forkName.unicode, fsRdPerm, &srcRefNum);
1169 if( err == noErr ) /* Write the fork to disk */
1170 err = WriteFork( srcRefNum, destRefNum, params, forkSize );
1171
1172 if( destRefNum != 0 ) /* Close the destination fork */
1173 myverify_noerr( FSCloseFork( destRefNum ) );
1174 if( srcRefNum != 0 ) /* Close the source fork */
1175 myverify_noerr( FSCloseFork( srcRefNum ) );
1176 }
1177 }
1178 while( err == noErr );
1179
1180 if( err == errFSNoMoreItems )
1181 err = noErr;
1182
1183 mycheck_noerr( err );
1184
1185 return err;
1186}
1187
1188 /* If we're copying to a DropBox, we have to handle the copy process a little */
1189 /* differently then when we are copying to a regular folder. */
1190static OSErr CopyForksToDropBox( const FSRef *source,
1191 const FSRef *dest,
1192 CopyParams *params )
1193{
1194 GenLinkedList forkList;
1195 GenIteratorPtr pIter;
1196 ForkTrackerPtr forkPtr;
1197 SInt16 srcRefNum;
1198 OSErr err;
1199
1200 InitLinkedList( &forkList, MyCloseForkProc );
1201
1202 /* If we're copying into a drop folder, open up all of those forks. */
1203 /* We have to do this because once we've started writing to a fork */
1204 /* in a drop folder, we can't open any more forks. */
1205 err = OpenAllForks( dest, &forkList );
1206
1207 /* Copy each fork over to the destination */
1208 for( InitIterator( &forkList, &pIter ); pIter != NULL && err == noErr; Next( &pIter ) )
1209 {
1210 srcRefNum = 0;
1211 forkPtr = GetData( pIter );
1212 /* Open the source fork */
1213 err = FSOpenFork(source, forkPtr->forkName.length, forkPtr->forkName.unicode, fsRdPerm, &srcRefNum);
1214 if( err == noErr ) /* Write the data over */
1215 err = WriteFork( srcRefNum, forkPtr->forkDestRefNum, params, forkPtr->forkSize );
1216
1217 if( srcRefNum != 0 ) /* Close the source fork */
1218 myverify_noerr( FSCloseFork( srcRefNum ) );
1219 }
1220 /* we're done, so destroy the list even if an error occured */
1221 /* the DisposeDataProc will close any open forks */
1222 DestroyList( &forkList );
1223
1224 mycheck_noerr( err );
1225
1226 return err;
1227}
1228
1229/*****************************************************************************/
1230
1231 /* Create and open all the forks in the destination file. We need to do this when */
1232 /* we're copying into a drop folder, where you must open all the forks before starting */
1233 /* to write to any of them. */
1234 /* */
1235 /* IMPORTANT: If it fails, this routine won't close forks that opened successfully. */
1236 /* Make sure that the DisposeDataProc for the forkList closed any open forks */
1237 /* Or you close each one manually before destroying the list */
1238static OSErr OpenAllForks( const FSRef *dest,
1239 GenLinkedList *forkList )
1240{
1241 ForkTrackerPtr forkPtr;
1242 HFSUniStr255 forkName;
1243 CatPositionRec iterator;
1244 SInt64 forkSize;
1245 OSErr err = ( dest != NULL && forkList != NULL ) ? noErr : paramErr;
1246
1247 /* need to initialize the iterator before using it */
1248 iterator.initialize = 0;
1249
1250 /* Iterate over the list of forks */
1251 while( err == noErr )
1252 {
1253 forkPtr = NULL; /* init forkPtr */
1254
1255 err = FSIterateForks( dest, &iterator, &forkName, &forkSize, NULL );
1256 if( err == noErr )
1257 err = ( forkPtr = (ForkTrackerPtr) NewPtr( sizeof( ForkTracker ) ) ) != NULL ? noErr : MemError();
1258 if( err == noErr )
1259 {
1260 forkPtr->forkName = forkName;
1261 forkPtr->forkSize = forkSize;
1262 forkPtr->forkDestRefNum = 0;
1263
1264 /* Create the fork. Note: Data and Resource forks are automatically */
1265 /* created when the file is created. FSCreateFork returns noErr for them */
1266 /* We also want to create the fork even if there is no data to preserve */
1267 /* empty forks */
1268 err = FSCreateFork( dest, forkName.length, forkName.unicode );
1269
1270 /* Swallow afpAccessDenied because this operation causes the external file */
1271 /* system compatibility shim in Mac OS 9 to generate a GetCatInfo request */
1272 /* to the AppleShare external file system, which in turn causes an AFP */
1273 /* GetFileDirParms request on the wire, which the AFP server bounces with */
1274 /* afpAccessDenied because the file is in a drop folder. As there's no */
1275 /* native support for non-classic forks in current AFP, there's no way I */
1276 /* can decide how I should handle this in a non-test case. So I just */
1277 /* swallow the error and hope that when native AFP support arrives, the */
1278 /* right thing will happen. */
1279 if( err == afpAccessDenied )
1280 err = noErr;
1281
1282 /* only open the fork if the fork has some data */
1283 if( err == noErr && forkPtr->forkSize > 0 )
1284 err = FSOpenFork( dest, forkPtr->forkName.length, forkPtr->forkName.unicode, fsWrPerm, &forkPtr->forkDestRefNum );
1285
1286 /* if everything is ok, add this fork to the list */
1287 if( err == noErr )
1288 err = AddToTail( forkList, forkPtr );
1289 }
1290
1291 if( err != noErr && forkPtr != NULL )
1292 DisposePtr( (char*) forkPtr );
1293 }
1294
1295 if( err == errFSNoMoreItems )
1296 err = noErr;
1297
1298 mycheck_noerr( err );
1299
1300 return err;
1301}
1302
1303/*****************************************************************************/
1304
1305 /* Writes the fork from the source, references by srcRefNum, to the destination fork */
1306 /* references by destRefNum */
1307static OSErr WriteFork( const SInt16 srcRefNum,
1308 const SInt16 destRefNum,
1309 const CopyParams *params,
1310 const SInt64 forkSize )
1311{
1312 UInt64 bytesRemaining;
1313 UInt64 bytesToReadThisTime;
1314 UInt64 bytesToWriteThisTime;
1315 OSErr err;
1316
1317
1318 /* Here we create space for the entire fork on the destination volume. */
1319 /* FSAllocateFork has the right semantics on both traditional Mac OS */
1320 /* and Mac OS X. On traditional Mac OS it will allocate space for the */
1321 /* file in one hit without any other special action. On Mac OS X, */
1322 /* FSAllocateFork is preferable to FSSetForkSize because it prevents */
1323 /* the system from zero filling the bytes that were added to the end */
1324 /* of the fork (which would be waste because we're about to write over */
1325 /* those bytes anyway. */
1326 err = FSAllocateFork(destRefNum, kFSAllocNoRoundUpMask, fsFromStart, 0, forkSize, NULL);
1327
1328 /* Copy the file from the source to the destination in chunks of */
1329 /* no more than params->copyBufferSize bytes. This is fairly */
1330 /* boring code except for the bytesToReadThisTime/bytesToWriteThisTime */
1331 /* distinction. On the last chunk, we round bytesToWriteThisTime */
1332 /* up to the next 512 byte boundary and then, after we exit the loop, */
1333 /* we set the file's EOF back to the real location (if the fork size */
1334 /* is not a multiple of 512 bytes). */
1335 /* */
1336 /* This technique works around a 'bug' in the traditional Mac OS File Manager, */
1337 /* where the File Manager will put the last 512-byte block of a large write into */
1338 /* the cache (even if we specifically request no caching) if that block is not */
1339 /* full. If the block goes into the cache it will eventually have to be */
1340 /* flushed, which causes sub-optimal disk performance. */
1341 /* */
1342 /* This is only done if the destination volume is local. For a network */
1343 /* volume, it's better to just write the last bytes directly. */
1344 /* */
1345 /* This is extreme over-optimization given the other limits of this */
1346 /* sample, but I will hopefully get to the other limits eventually. */
1347 bytesRemaining = forkSize;
1348 while( err == noErr && bytesRemaining != 0 )
1349 {
1350 if( bytesRemaining > params->copyBufferSize )
1351 {
1352 bytesToReadThisTime = params->copyBufferSize;
1353 bytesToWriteThisTime = bytesToReadThisTime;
1354 }
1355 else
1356 {
1357 bytesToReadThisTime = bytesRemaining;
1358 bytesToWriteThisTime = ( params->copyingToLocalVolume ) ?
1359 ( (bytesRemaining + 0x01FF ) & ~0x01FF ) : bytesRemaining;
1360 }
1361
1362 err = FSReadFork( srcRefNum, fsAtMark + noCacheMask, 0, bytesToReadThisTime, params->copyBuffer, NULL );
1363 if( err == noErr )
1364 err = FSWriteFork( destRefNum, fsAtMark + noCacheMask, 0, bytesToWriteThisTime, params->copyBuffer, NULL );
1365 if( err == noErr )
1366 bytesRemaining -= bytesToReadThisTime;
1367 }
1368
1369 if (err == noErr && params->copyingToLocalVolume && ( forkSize & 0x01FF ) != 0 )
1370 err = FSSetForkSize( destRefNum, fsFromStart, forkSize );
1371
1372 return err;
1373}
1374
1375/*****************************************************************************/
1376
1377#pragma mark ----- Calculate Buffer Size -----
1378
1379 /* This routine calculates the appropriate buffer size for */
1380 /* the given volParms. It's a simple composition of FSGetVolParms */
1381 /* BufferSizeForVolSpeed. */
1382static UInt32 CalcBufferSizeForVol(const GetVolParmsInfoBuffer *volParms, UInt32 volParmsSize)
1383{
1384 UInt32 volumeBytesPerSecond = 0;
1385
1386 /* Version 1 of the GetVolParmsInfoBuffer included the vMAttrib */
1387 /* field, so we don't really need to test actualSize. A noErr */
1388 /* result indicates that we have the info we need. This is */
1389 /* just a paranoia check. */
1390
1391 mycheck(volParmsSize >= offsetof(GetVolParmsInfoBuffer, vMVolumeGrade));
1392
1393 /* On the other hand, vMVolumeGrade was not introduced until */
1394 /* version 2 of the GetVolParmsInfoBuffer, so we have to explicitly */
1395 /* test whether we got a useful value. */
1396
1397 if( ( volParmsSize >= offsetof(GetVolParmsInfoBuffer, vMForeignPrivID) ) &&
1398 ( volParms->vMVolumeGrade <= 0 ) )
1399 {
1400 volumeBytesPerSecond = -volParms->vMVolumeGrade;
1401 }
1402
1403 return BufferSizeForVolSpeed(volumeBytesPerSecond);
1404}
1405
1406/*****************************************************************************/
1407
1408 /* Calculate an appropriate copy buffer size based on the volumes */
1409 /* rated speed. Our target is to use a buffer that takes 0.25 */
1410 /* seconds to fill. This is necessary because the volume might be */
1411 /* mounted over a very slow link (like ARA), and if we do a 256 KB */
1412 /* read over an ARA link we'll block the File Manager queue for */
1413 /* so long that other clients (who might have innocently just */
1414 /* called PBGetCatInfoSync) will block for a noticeable amount of time. */
1415 /* */
1416 /* Note that volumeBytesPerSecond might be 0, in which case we assume */
1417 /* some default value. */
1418static UInt32 BufferSizeForVolSpeed(UInt32 volumeBytesPerSecond)
1419{
1420 ByteCount bufferSize;
1421
1422 if (volumeBytesPerSecond == 0)
1423 bufferSize = kDefaultCopyBufferSize;
1424 else
1425 { /* We want to issue a single read that takes 0.25 of a second, */
1426 /* so devide the bytes per second by 4. */
1427 bufferSize = volumeBytesPerSecond / 4;
1428 }
1429
1430 /* Round bufferSize down to 512 byte boundary. */
1431 bufferSize &= ~0x01FF;
1432
1433 /* Clip to sensible limits. */
1434 if (bufferSize < kMinimumCopyBufferSize)
1435 bufferSize = kMinimumCopyBufferSize;
1436 else if (bufferSize > kMaximumCopyBufferSize)
1437 bufferSize = kMaximumCopyBufferSize;
1438
1439 return bufferSize;
1440}
1441
1442/*****************************************************************************/
1443
1444#pragma mark ----- Delete Objects -----
1445
1446OSErr FSDeleteObjects( const FSRef *source )
1447{
1448 FSCatalogInfo catalogInfo;
1449 OSErr err = ( source != NULL ) ? noErr : paramErr;
1450
1451 #if DEBUG && !TARGET_API_MAC_OS8
1452 if( err == noErr )
1453 {
1454 char path[1024];
1455 myverify_noerr(FSRefMakePath( source, (unsigned char*)path, 1024 ));
1456 dwarning(("\n%s -- Deleting %s\n", __FUNCTION__, path));
1457 }
1458 #endif
1459
1460 /* get nodeFlags for container */
1461 if( err == noErr )
1462 err = FSGetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL,NULL);
1463 if( err == noErr && (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0 )
1464 { /* its a directory, so delete its contents before we delete it */
1465 err = FSDeleteFolder(source);
1466 }
1467 if( err == noErr && (catalogInfo.nodeFlags & kFSNodeLockedMask) != 0 ) /* is object locked? */
1468 { /* then attempt to unlock the object (ignore err since FSDeleteObject will set it correctly) */
1469 catalogInfo.nodeFlags &= ~kFSNodeLockedMask;
1470 (void) FSSetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo);
1471 }
1472 if( err == noErr ) /* delete the object (if it was a directory it is now empty, so we can delete it) */
1473 err = FSDeleteObject(source);
1474
1475 mycheck_noerr( err );
1476
1477 return ( err );
1478}
1479
1480/*****************************************************************************/
1481
1482#pragma mark ----- Delete Folders -----
1483
1484static OSErr FSDeleteFolder( const FSRef *container )
1485{
1486 FSDeleteObjectGlobals theGlobals;
1487
1488 theGlobals.result = ( container != NULL ) ? noErr : paramErr;
1489
1490 /* delete container's contents */
1491 if( theGlobals.result == noErr )
1492 FSDeleteFolderLevel(container, &theGlobals);
1493
1494 mycheck_noerr( theGlobals.result );
1495
1496 return ( theGlobals.result );
1497}
1498
1499/*****************************************************************************/
1500
1501static void FSDeleteFolderLevel(const FSRef *container,
1502 FSDeleteObjectGlobals *theGlobals )
1503{
1504 FSIterator iterator;
1505 FSRef itemToDelete;
1506 UInt16 nodeFlags;
1507
1508 /* Open FSIterator for flat access and give delete optimization hint */
1509 theGlobals->result = FSOpenIterator(container, kFSIterateFlat + kFSIterateDelete, &iterator);
1510 if ( theGlobals->result == noErr )
1511 {
1512 do /* delete the contents of the directory */
1513 {
1514 /* get 1 item to delete */
1515 theGlobals->result = FSGetCatalogInfoBulk( iterator, 1, &theGlobals->actualObjects,
1516 NULL, kFSCatInfoNodeFlags, &theGlobals->catalogInfo,
1517 &itemToDelete, NULL, NULL);
1518 if ( (theGlobals->result == noErr) && (theGlobals->actualObjects == 1) )
1519 {
1520 /* save node flags in local in case we have to recurse */
1521 nodeFlags = theGlobals->catalogInfo.nodeFlags;
1522
1523 /* is it a directory? */
1524 if ( (nodeFlags & kFSNodeIsDirectoryMask) != 0 )
1525 { /* yes -- delete its contents before attempting to delete it */
1526 FSDeleteFolderLevel(&itemToDelete, theGlobals);
1527 }
1528 if ( theGlobals->result == noErr) /* are we still OK to delete? */
1529 {
1530 if ( (nodeFlags & kFSNodeLockedMask) != 0 ) /* is item locked? */
1531 { /* then attempt to unlock it (ignore result since FSDeleteObject will set it correctly) */
1532 theGlobals->catalogInfo.nodeFlags = nodeFlags & ~kFSNodeLockedMask;
1533 (void) FSSetCatalogInfo(&itemToDelete, kFSCatInfoNodeFlags, &theGlobals->catalogInfo);
1534 }
1535 /* delete the item */
1536 theGlobals->result = FSDeleteObject(&itemToDelete);
1537 }
1538 }
1539 } while ( theGlobals->result == noErr );
1540
1541 /* we found the end of the items normally, so return noErr */
1542 if ( theGlobals->result == errFSNoMoreItems )
1543 theGlobals->result = noErr;
1544
1545 /* close the FSIterator (closing an open iterator should never fail) */
1546 myverify_noerr(FSCloseIterator(iterator));
1547 }
1548
1549 mycheck_noerr( theGlobals->result );
1550
1551 return;
1552}
1553
1554/*****************************************************************************/
1555
1556#pragma mark ----- Utilities -----
1557
1558 /* Figures out if the given directory is a drop box or not */
1559 /* if it is, the Copy Engine will behave slightly differently */
1560static OSErr IsDropBox( const FSRef* source,
1561 Boolean *isDropBox )
1562{
1563 FSCatalogInfo tmpCatInfo;
1564 FSSpec sourceSpec;
1565 Boolean isDrop = false;
1566 OSErr err;
1567
1568 /* get info about the destination, and an FSSpec to it for PBHGetDirAccess */
1569 err = FSGetCatalogInfo(source, kFSCatInfoNodeFlags | kFSCatInfoPermissions, &tmpCatInfo, NULL, &sourceSpec, NULL);
1570 if( err == noErr ) /* make sure the source is a directory */
1571 err = ((tmpCatInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) ? noErr : errFSNotAFolder;
1572 if( err == noErr )
1573 {
1574 HParamBlockRec hPB;
1575
1576 BlockZero( &hPB, sizeof( HParamBlockRec ) );
1577
1578 hPB.accessParam.ioNamePtr = sourceSpec.name;
1579 hPB.accessParam.ioVRefNum = sourceSpec.vRefNum;
1580 hPB.accessParam.ioDirID = sourceSpec.parID;
1581
1582 /* This is the official way (reads: the way X Finder does it) to figure */
1583 /* out the current users access privileges to a given directory */
1584 err = PBHGetDirAccessSync(&hPB);
1585 if( err == noErr ) /* its a drop folder if the current user only has write access */
1586 isDrop = (hPB.accessParam.ioACAccess & kPrivilegesMask) == kioACAccessUserWriteMask;
1587 else if ( err == paramErr )
1588 {
1589 /* There is a bug (2908703) in the Classic File System (not OS 9.x or Carbon) */
1590 /* on 10.1.x where PBHGetDirAccessSync sometimes returns paramErr even when the */
1591 /* data passed in is correct. This is a workaround/hack for that problem, */
1592 /* but is not as accurate. */
1593 /* Basically, if "Everyone" has only Write/Search access then its a drop folder */
1594 /* that is the most common case when its a drop folder */
1595 FSPermissionInfo *tmpPerm = (FSPermissionInfo *)tmpCatInfo.permissions;
1596 isDrop = ((tmpPerm->mode & kRWXOtherAccessMask) == kDropFolderValue);
1597 err = noErr;
1598 }
1599 }
1600
1601 *isDropBox = isDrop;
1602
1603 mycheck_noerr( err );
1604
1605 return err;
1606}
1607
1608/*****************************************************************************/
1609
1610 /* The copy engine is going to set the item's creation date */
1611 /* to kMagicBusyCreationDate while it's copying the item. */
1612 /* But kMagicBusyCreationDate is an old-style 32-bit date/time, */
1613 /* while the HFS Plus APIs use the new 64-bit date/time. So */
1614 /* we have to call a happy UTC utilities routine to convert from */
1615 /* the local time kMagicBusyCreationDate to a UTCDateTime */
1616 /* gMagicBusyCreationDate, which the File Manager will store */
1617 /* on disk and which the Finder we read back using the old */
1618 /* APIs, whereupon the File Manager will convert it back */
1619 /* to local time (and hopefully get the kMagicBusyCreationDate */
1620 /* back!). */
1621static OSErr GetMagicBusyCreateDate( UTCDateTime *date )
1622{
1623 static UTCDateTime magicDate = { 0, 0xDEADBEEF, 0 };
1624 OSErr err = ( date != NULL ) ? noErr : paramErr;
1625
1626 if( err == noErr && magicDate.lowSeconds == 0xDEADBEEF )
1627 err = ConvertLocalTimeToUTC( kMagicBusyCreationDate, &magicDate.lowSeconds );
1628 if( err == noErr )
1629 *date = magicDate;
1630
1631 mycheck_noerr( err );
1632
1633 return err;
1634}
1635
1636/*****************************************************************************/
1637
1638static OSErr FSGetVRefNum( const FSRef *ref,
1639 FSVolumeRefNum *vRefNum)
1640{
1641 FSCatalogInfo catalogInfo;
1642 OSErr err = ( ref != NULL && vRefNum != NULL ) ? noErr : paramErr;
1643
1644 if( err == noErr ) /* get the volume refNum from the FSRef */
1645 err = FSGetCatalogInfo(ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
1646 if( err == noErr )
1647 *vRefNum = catalogInfo.volume;
1648
1649 mycheck_noerr( err );
1650
1651 return err;
1652}
1653
1654/*****************************************************************************/
1655
1656static OSErr FSGetVolParms( FSVolumeRefNum volRefNum,
1657 UInt32 bufferSize,
1658 GetVolParmsInfoBuffer *volParmsInfo,
1659 UInt32 *actualInfoSize) /* Can Be NULL */
1660{
1661 HParamBlockRec pb;
1662 OSErr err = ( volParmsInfo != NULL ) ? noErr : paramErr;
1663
1664 if( err == noErr )
1665 {
1666 pb.ioParam.ioNamePtr = NULL;
1667 pb.ioParam.ioVRefNum = volRefNum;
1668 pb.ioParam.ioBuffer = (Ptr)volParmsInfo;
1669 pb.ioParam.ioReqCount = (SInt32)bufferSize;
1670 err = PBHGetVolParmsSync(&pb);
1671 }
1672 /* return number of bytes the file system returned in volParmsInfo buffer */
1673 if( err == noErr && actualInfoSize != NULL)
1674 *actualInfoSize = (UInt32)pb.ioParam.ioActCount;
1675
1676 mycheck_noerr( err );
1677
1678 return ( err );
1679}
1680
1681/*****************************************************************************/
1682
1683/* Converts a unicode string to a PString */
1684/* If your code is only for OS X, you can use CFString functions to do all this */
1685/* Since this sample code supports OS 9.1 -> OS X, I have to do this the */
1686/* old fashioned way. */
1687static OSErr UniStrToPStr( const HFSUniStr255 *uniStr,
1688 TextEncoding textEncodingHint,
1689 Boolean isVolumeName,
1690 Str255 pStr )
1691{
1692 UnicodeMapping uMapping;
1693 UnicodeToTextInfo utInfo;
1694 ByteCount unicodeByteLength = 0;
1695 ByteCount unicodeBytesConverted;
1696 ByteCount actualPascalBytes;
1697 OSErr err = (uniStr != NULL && pStr != NULL) ? noErr : paramErr;
1698
1699 /* make sure output is valid in case we get errors or there's nothing to convert */
1700 pStr[0] = 0;
1701
1702 if( err == noErr )
1703 unicodeByteLength = uniStr->length * sizeof(UniChar); /* length can be zero, which is fine */
1704 if( err == noErr && unicodeByteLength != 0 )
1705 {
1706 /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */
1707 if ( kTextEncodingUnknown == textEncodingHint )
1708 {
1709 ScriptCode script;
1710 RegionCode region;
1711
1712 script = (ScriptCode)GetScriptManagerVariable(smSysScript);
1713 region = (RegionCode)GetScriptManagerVariable(smRegionCode);
1714 err = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare,
1715 region, NULL, &textEncodingHint );
1716 if ( err == paramErr )
1717 { /* ok, ignore the region and try again */
1718 err = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare,
1719 kTextRegionDontCare, NULL,
1720 &textEncodingHint );
1721 }
1722 if ( err != noErr ) /* ok... try something */
1723 textEncodingHint = kTextEncodingMacRoman;
1724 }
1725
1726 uMapping.unicodeEncoding = CreateTextEncoding( kTextEncodingUnicodeV2_0,
1727 kUnicodeCanonicalDecompVariant,
1728 kUnicode16BitFormat);
1729 uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint);
1730 uMapping.mappingVersion = kUnicodeUseHFSPlusMapping;
1731
1732 err = CreateUnicodeToTextInfo(&uMapping, &utInfo);
1733 if( err == noErr )
1734 {
1735 err = ConvertFromUnicodeToText( utInfo, unicodeByteLength, uniStr->unicode, kUnicodeLooseMappingsMask,
1736 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */
1737 isVolumeName ? kHFSMaxVolumeNameChars : kHFSPlusMaxFileNameChars,
1738 &unicodeBytesConverted, &actualPascalBytes, &pStr[1]);
1739 }
1740 if( err == noErr )
1741 pStr[0] = actualPascalBytes;
1742
1743 /* verify the result in debug builds -- there's really not anything you can do if it fails */
1744 myverify_noerr(DisposeUnicodeToTextInfo(&utInfo));
1745 }
1746
1747 mycheck_noerr( err );
1748
1749 return ( err );
1750}
1751
1752/*****************************************************************************/
1753
1754 /* Yeah I know there is FSpMakeFSRef, but this way I don't have to */
1755 /* actually have an FSSpec created to make the FSRef, and this is */
1756 /* what FSpMakeFSRef does anyways */
1757static OSErr FSMakeFSRef( FSVolumeRefNum volRefNum,
1758 SInt32 dirID,
1759 ConstStr255Param name,
1760 FSRef *ref )
1761{
1762 FSRefParam pb;
1763 OSErr err = ( ref != NULL ) ? noErr : paramErr;
1764
1765 if( err == noErr )
1766 {
1767 pb.ioVRefNum = volRefNum;
1768 pb.ioDirID = dirID;
1769 pb.ioNamePtr = (StringPtr)name;
1770 pb.newRef = ref;
1771 err = PBMakeFSRefSync(&pb);
1772 }
1773
1774 mycheck_noerr( err );
1775
1776 return ( err );
1777}
1778
1779/*****************************************************************************/
1780
1781 /* This checks the destination to see if an object of the same name as the source */
1782 /* exists or not. If it does we have to special handle the DupeActions */
1783 /* */
1784 /* If kDupeActionReplace we move aside the object by renameing it to ".DeleteMe" */
1785 /* so that it will be invisible (X only), and give a suggestion on what to do with */
1786 /* it if for some unknown reason it survives the copy and the user finds it. This */
1787 /* rename is mainly done to handle the case where the source is in the destination */
1788 /* and the user wants to replace. Basically keeping the source around throughout */
1789 /* the copy, deleting it afterwards. Its also done cause its a good idea not to */
1790 /* dispose of the existing object in case the copy fails */
1791 /* */
1792 /* If kDupeActionRename, we create a unique name for the new object and pass */
1793 /* it back to the caller */
1794static OSErr SetupDestination( const FSRef *destDir,
1795 const DupeAction dupeAction,
1796 HFSUniStr255 *sourceName,
1797 FSRef *deleteMeRef,
1798 Boolean *isReplacing )
1799{
1800 FSRef tmpRef;
1801 OSErr err;
1802
1803 /* check if an object of the same name already exists in the destination */
1804 err = FSMakeFSRefUnicode( destDir, sourceName->length, sourceName->unicode, kTextEncodingUnknown, &tmpRef );
1805 if( err == noErr )
1806 { /* if the user wants to replace the existing */
1807 /* object, rename it to .DeleteMe first. Delete it */
1808 if( dupeAction == kDupeActionReplace ) /* only after copying the new one successfully */
1809 {
1810 err = FSRenameUnicode( &tmpRef, 9, (UniChar*)"\0.\0D\0e\0l\0e\0t\0e\0M\0e", kTextEncodingMacRoman, deleteMeRef );
1811 *isReplacing = ( err == noErr ) ? true : false;
1812 }
1813 else if( dupeAction == kDupeActionRename ) /* if the user wants to just rename it */
1814 err = GetUniqueName( destDir, sourceName ); /* then we get a unique name for the new object */
1815 }
1816 else if ( err == fnfErr ) /* if no object exists then */
1817 err = noErr; /* continue with no error */
1818
1819 return err;
1820}
1821
1822/*****************************************************************************/
1823
1824 /* Given a directory and a name, GetUniqueName will check if an object */
1825 /* with the same name already exists, and if it does it will create */
1826 /* a new, unique name for and return it. */
1827 /* it simply appends a number to the end of the name. It is not */
1828 /* fool proof, and it is limited... I'll take care of that in a */
1829 /* later release */
1830 /* If anyone has any suggestions/better techniques I would love to hear */
1831 /* about them */
1832static OSErr GetUniqueName( const FSRef *destDir,
1833 HFSUniStr255 *sourceName )
1834{
1835 HFSUniStr255 tmpName = *sourceName;
1836 FSRef tmpRef;
1837 unsigned char hexStr[17] = "123456789"; /* yeah, only 9... I'm lazy, sosumi */
1838 long count = 0;
1839 int index;
1840 OSErr err;
1841
1842 /* find the dot, if there is one */
1843 for( index = tmpName.length; index >= 0 && tmpName.unicode[index] != (UniChar) '.'; index-- ) { /* Do Nothing */ }
1844
1845 if( index <= 0) /* no dot or first char is a dot (invisible file), so append to end of name */
1846 index = tmpName.length;
1847 else /* shift the extension up two spots to make room for our digits */
1848 BlockMoveData( tmpName.unicode + index, tmpName.unicode + index + 2, (tmpName.length - index) * 2 );
1849
1850 /* add the space to the name */
1851 tmpName.unicode[ index ] = (UniChar)' ';
1852 /* we're adding two characters to the name */
1853 tmpName.length += 2;
1854
1855 do { /* add the digit to the name */
1856 tmpName.unicode[ index + 1 ] = hexStr[count];
1857 /* check if the file with this new name already exists */
1858 err = FSMakeFSRefUnicode( destDir, tmpName.length, tmpName.unicode, kTextEncodingUnknown, &tmpRef );
1859 count++;
1860 } while( err == noErr && count < 10 );
1861
1862 if( err == fnfErr )
1863 {
1864 err = noErr;
1865 *sourceName = tmpName;
1866 }
1867
1868 return err;
1869}
1870
1871/*****************************************************************************/
1872
1873static OSErr GetObjectName( const FSRef *sourceRef,
1874 HFSUniStr255 *sourceName, /* can be NULL */
1875 TextEncoding *sourceEncoding ) /* can be NULL */
1876{
1877 FSCatalogInfo catInfo;
1878 FSCatalogInfoBitmap whichInfo = (sourceEncoding != NULL) ? kFSCatInfoTextEncoding : kFSCatInfoNone;
1879 OSErr err;
1880
1881 err = FSGetCatalogInfo( sourceRef, whichInfo, &catInfo, sourceName, NULL, NULL );
1882 if( err == noErr && sourceEncoding != NULL )
1883 *sourceEncoding = catInfo.textEncodingHint;
1884
1885 return err;
1886}
1887
1888/*****************************************************************************/
1889
1890static OSErr CreateFolder( const FSRef *sourceRef,
1891 const FSRef *destDirRef,
1892 const FSCatalogInfo *catalogInfo,
1893 const HFSUniStr255 *folderName,
1894 CopyParams *params,
1895 FSRef *newFSRefPtr,
1896 FSSpec *newFSSpecPtr )
1897{
1898 FSCatalogInfo tmpCatInfo;
1899 FSPermissionInfo origPermissions;
1900 OSErr err = ( sourceRef != NULL && destDirRef != NULL && catalogInfo != NULL &&
1901 folderName != NULL && newFSRefPtr != NULL ) ? noErr : paramErr;
1902
1903 if( err == noErr )
1904 { /* store away the catInfo, create date and permissions on the orig folder */
1905 tmpCatInfo = *catalogInfo;
1906 origPermissions = *((FSPermissionInfo*)catalogInfo->permissions);
1907 }
1908 if( err == noErr ) /* create the new folder */
1909 err = DoCreateFolder( sourceRef, destDirRef, &tmpCatInfo, folderName, params, newFSRefPtr, newFSSpecPtr );
1910 if( err == noErr && !params->copyingToDropFolder )
1911 { /* if its not a drop box, set the permissions on the new folder */
1912 *((FSPermissionInfo*)tmpCatInfo.permissions) = origPermissions;
1913 err = FSSetCatalogInfo( newFSRefPtr, kFSCatInfoPermissions, &tmpCatInfo );
1914 }
1915
1916 mycheck_noerr( err );
1917
1918 return err;
1919}
1920
1921/*****************************************************************************/
1922
1923static OSErr DoCreateFolder(const FSRef *sourceRef,
1924 const FSRef *destDirRef,
1925 const FSCatalogInfo *catalogInfo,
1926 const HFSUniStr255 *folderName,
1927 CopyParams *params,
1928 FSRef *newFSRefPtr,
1929 FSSpec *newFSSpecPtr)
1930{
1931 FSCatalogInfo catInfo = *catalogInfo;
1932 OSErr err;
1933
1934 /* Clear the "inited" bit so that the Finder positions the icon for us. */
1935 ((FInfo *)(catInfo.finderInfo))->fdFlags &= ~kHasBeenInited;
1936
1937 /* we need to have user level read/write/execute access to the folder we are going to create, */
1938 /* otherwise FSCreateDirectoryUnicode will return -5000 (afpAccessDenied), */
1939 /* and the FSRef returned will be invalid, yet the folder is created... bug? */
1940 ((FSPermissionInfo*) catInfo.permissions)->mode |= kRWXUserAccessMask;
1941
1942 err = FSCreateDirectoryUnicode( destDirRef, folderName->length,
1943 folderName->unicode, kFSCatInfoSettableInfo,
1944 &catInfo, newFSRefPtr,
1945 newFSSpecPtr, NULL);
1946
1947 /* With the new APIs, folders can have forks as well as files. Before */
1948 /* we start copying items in the folder, we must copy over the forks */
1949 /* Currently, MacOS doesn't support any file systems that have forks in */
1950 /* folders, but the API supports it so (for possible future */
1951 /* compatability) I kept this in here. */
1952 if( err == noErr )
1953 err = CopyForks( sourceRef, newFSRefPtr, params );
1954
1955 mycheck_noerr( err );
1956
1957 return err;
1958}
1959
1960/*****************************************************************************/
1961
1962 /* This is the DisposeDataProc that is used by the GenLinkedList in FSCopyFolder */
1963 /* Simply disposes of the data we created and returns */
1964static pascal void MyDisposeDataProc( void *pData )
1965{
1966 if( pData != NULL )
1967 DisposePtr( (char*) pData );
1968}
1969
1970/*****************************************************************************/
1971
1972 /* This is the DisposeDataProc that is used by the GenLinkedList in CopyItemForks */
1973 /* Simply closes the resource fork (if opened, != 0) and disposes of the memory */
1974static pascal void MyCloseForkProc( void *pData )
1975{
1976 SInt16 refNum;
1977
1978 if( pData == NULL )
1979 return;
1980
1981 refNum = ((ForkTrackerPtr)pData)->forkDestRefNum;
1982 if( refNum != 0 )
1983 myverify_noerr( FSCloseFork( refNum ) ); /* the fork was opened, so close it */
1984
1985 DisposePtr( (char*) pData );
1986} \ No newline at end of file
diff --git a/linden/indra/mac_updater/FSCopyObject.h b/linden/indra/mac_updater/FSCopyObject.h
new file mode 100644
index 0000000..6cbbf94
--- /dev/null
+++ b/linden/indra/mac_updater/FSCopyObject.h
@@ -0,0 +1,285 @@
1/**
2 * @file FSCopyObject.h
3 * @brief Mac OSX updater program
4 *
5 * Copyright (c) 2004-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28/*
29 File: FSCopyObject.h
30
31 Contains: A Copy/Delete Files/Folders engine which uses the HFS+ API's
32
33 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
34 ("Apple") in consideration of your agreement to the following terms, and your
35 use, installation, modification or redistribution of this Apple software
36 constitutes acceptance of these terms. If you do not agree with these terms,
37 please do not use, install, modify or redistribute this Apple software.
38
39 In consideration of your agreement to abide by the following terms, and subject
40 to these terms, Apple grants you a personal, non-exclusive license, under Apple�s
41 copyrights in this original Apple software (the "Apple Software"), to use,
42 reproduce, modify and redistribute the Apple Software, with or without
43 modifications, in source and/or binary forms; provided that if you redistribute
44 the Apple Software in its entirety and without modifications, you must retain
45 this notice and the following text and disclaimers in all such redistributions of
46 the Apple Software. Neither the name, trademarks, service marks or logos of
47 Apple Computer, Inc. may be used to endorse or promote products derived from the
48 Apple Software without specific prior written permission from Apple. Except as
49 expressly stated in this notice, no other rights or licenses, express or implied,
50 are granted by Apple herein, including but not limited to any patent rights that
51 may be infringed by your derivative works or by other works in which the Apple
52 Software may be incorporated.
53
54 The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
55 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
56 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
58 COMBINATION WITH YOUR PRODUCTS.
59
60 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
61 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
62 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
63 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
64 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
65 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
66 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
67
68 Copyright � 2002-2004 Apple Computer, Inc., All Rights Reserved
69*/
70
71
72#ifndef __FSCOPYOBJECT_H__
73#define __FSCOPYOBJECT_H__
74
75#ifdef __cplusplus
76extern "C" {
77#endif
78
79#if TARGET_API_MAC_OSX || defined( __APPLE_CC__ )
80#include <CoreServices/CoreServices.h>
81#endif
82
83#define DEBUG 1 /* set to zero if you don't want debug spew */
84
85#if DEBUG
86 #include <stdio.h>
87
88 #define QuoteExceptionString(x) #x
89
90 #define dwarning(s) do { printf s; fflush(stderr); } while( 0 )
91
92 #define mycheck_noerr( error ) \
93 do { \
94 if( (OSErr) error != noErr ) { \
95 dwarning((QuoteExceptionString(error) " != noErr in File: %s, Function: %s, Line: %d, Error: %d\n", \
96 __FILE__, __FUNCTION__, __LINE__, (OSErr) error)); \
97 } \
98 } while( false )
99
100 #define mycheck( assertion ) \
101 do { \
102 if( ! assertion ) { \
103 dwarning((QuoteExceptionString(assertion) " failed in File: %s, Function: %s, Line: %d\n", \
104 __FILE__, __FUNCTION__, __LINE__)); \
105 } \
106 } while( false )
107
108 #define myverify(assertion) mycheck(assertion)
109 #define myverify_noerr(assertion) mycheck_noerr( (assertion) )
110#else
111 #define dwarning(s)
112
113 #define mycheck(assertion)
114 #define mycheck_noerr(err)
115 #define myverify(assertion) do { (void) (assertion); } while (0)
116 #define myverify_noerr(assertion) myverify(assertion)
117#endif
118
119/*
120 This code takes some tricks/techniques from MoreFilesX (by Jim Luther) and
121 MPFileCopy (by Quinn), wraps them all up into an easy to use API, and adds a bunch of
122 features and bug fixes. It will run on Mac OS 9.1 through 9.2.x and 10.1.x
123 and up (Classic, Carbon and Mach-O)
124*/
125
126 /* Different options that FSCopyObject can take during a copy */
127typedef UInt32 DupeAction;
128enum {
129 kDupeActionStandard, /* will do the copy with no frills */
130 kDupeActionReplace, /* will delete the existing object and then copy over the new one */
131 kDupeActionRename /* will rename the new object if an object of the same name exists */
132};
133
134/*****************************************************************************/
135
136#pragma mark CopyObjectFilterProcPtr
137
138/*
139 This is the prototype for the CallCopyObjectFilterProc function which
140 is called once for each file and directory found by FSCopyObject.
141 The CallCopyObjectFilterProc can use the read-only data it receives for
142 whatever it wants.
143
144 The result of the CallCopyObjectFilterProc function indicates if
145 the copy should be stopped. To stop the copy, return an error; to continue
146 the copy, return noErr.
147
148 The yourDataPtr parameter can point to whatever data structure you might
149 want to access from within the CallCopyObjectFilterProc.
150
151 Note: If an error had occured during the copy of the current object
152 (currentOSErr != noErr) the FSRef etc might not be valid
153
154 containerChanged --> Set to true if the container's contents changed
155 during iteration.
156 currentLevel --> The current recursion level into the container.
157 1 = the container, 2 = the container's immediate
158 subdirectories, etc.
159 currentOSErr --> The current error code, shows the results of the
160 copy of the current object (ref)
161 catalogInfo --> The catalog information for the current object.
162 Only the fields requested by the whichInfo
163 parameter passed to FSIterateContainer are valid.
164 ref --> The FSRef to the current object.
165 spec --> The FSSpec to the current object if the wantFSSpec
166 parameter passed to FSCopyObject is true.
167 name --> The name of the current object if the wantName
168 parameter passed to FSCopyObject is true.
169 yourDataPtr --> An optional pointer to whatever data structure you
170 might want to access from within the
171 CallCopyObjectFilterProc.
172 result <-- To continue the copy, return noErr
173
174 __________
175
176 Also see: FSCopyObject
177*/
178
179typedef CALLBACK_API( OSErr , CopyObjectFilterProcPtr ) (
180 Boolean containerChanged,
181 ItemCount currentLevel,
182 OSErr currentOSErr,
183 const FSCatalogInfo *catalogInfo,
184 const FSRef *ref,
185 const FSSpec *spec,
186 const HFSUniStr255 *name,
187 void *yourDataPtr);
188
189
190/*****************************************************************************/
191
192#pragma mark CallCopyObjectFilterProc
193
194#define CallCopyObjectFilterProc(userRoutine, containerChanged, currentLevel, currentOSErr, catalogInfo, ref, spec, name, yourDataPtr) \
195 (*(userRoutine))((containerChanged), (currentLevel), (currentOSErr), (catalogInfo), (ref), (spec), (name), (yourDataPtr))
196
197/*****************************************************************************/
198
199#pragma mark FSCopyObject
200
201/*
202 The FSCopyObject function takes a source object (can be a file or directory)
203 and copies it (and its contents if it's a directory) to the new destination
204 directory.
205
206 It will call your CopyObjectFilterProcPtr once for each object copied
207
208 The maxLevels parameter is only used when the object is a directory,
209 ignored otherwise.
210 It lets you control how deep the recursion goes.
211 If maxLevels is 1, FSCopyObject only scans the specified directory;
212 if maxLevels is 2, FSCopyObject scans the specified directory and
213 one subdirectory below the specified directory; etc. Set maxLevels to
214 zero to scan all levels.
215
216 The yourDataPtr parameter can point to whatever data structure you might
217 want to access from within your CopyObjectFilterProcPtr.
218
219 source --> The FSRef to the object you want to copy
220 destDir --> The FSRef to the directory you wish to copy source to
221 maxLevels --> Maximum number of directory levels to scan or
222 zero to scan all directory levels, ignored if the
223 object is a file
224 whichInfo --> The fields of the FSCatalogInfo you wish passed
225 to you in your CopyObjectFilterProc
226 dupeAction --> The action to take if an object of the same name exists
227 in the destination
228 newName --> The name you want the new object to have. If you pass
229 in NULL, the source object name will be used.
230 wantFSSpec --> Set to true if you want the FSSpec to each
231 object passed to your CopyObjectFilterProc.
232 wantName --> Set to true if you want the name of each
233 object passed to your CopyObjectFilterProc.
234 filterProcPtr --> A pointer to the CopyObjectFilterProc you
235 want called once for each object found
236 by FSCopyObject.
237 yourDataPtr --> An optional pointer to whatever data structure you
238 might want to access from within the
239 CopyObjectFilterProc.
240 newObjectRef --> An optional pointer to an FSRef that, on return,
241 references the new object. If you don't want this
242 info returned, pass in NULL
243 newObjectSpec --> An optional pointer to an FSSPec that, on return,
244 references the new object. If you don't want this
245 info returned, pass in NULL
246*/
247
248OSErr FSCopyObject( const FSRef *source,
249 const FSRef *destDir,
250 ItemCount maxLevels,
251 FSCatalogInfoBitmap whichInfo,
252 DupeAction dupeAction,
253 const HFSUniStr255 *newName, /* can be NULL */
254 Boolean wantFSSpec,
255 Boolean wantName,
256 CopyObjectFilterProcPtr filterProcPtr, /* can be NULL */
257 void *yourDataPtr, /* can be NULL */
258 FSRef *newObjectRef, /* can be NULL */
259 FSSpec *newObjectSpec); /* can be NULL */
260
261/*****************************************************************************/
262
263#pragma mark FSDeleteObjects
264
265/*
266 The FSDeleteObjects function takes an FSRef to a file or directory
267 and attempts to delete it. If the object is a directory, all files
268 and subdirectories in the specified directory are deleted. If a
269 locked file or directory is encountered, it is unlocked and then
270 deleted. After deleting the directory's contents, the directory
271 is deleted. If any unexpected errors are encountered,
272 FSDeleteContainer quits and returns to the caller.
273
274 source --> FSRef to an object (can be file or directory).
275
276 __________
277*/
278
279OSErr FSDeleteObjects( const FSRef *source );
280
281#ifdef __cplusplus
282}
283#endif
284
285#endif
diff --git a/linden/indra/mac_updater/GenLinkedList.c b/linden/indra/mac_updater/GenLinkedList.c
new file mode 100644
index 0000000..f1cde85
--- /dev/null
+++ b/linden/indra/mac_updater/GenLinkedList.c
@@ -0,0 +1,212 @@
1/*
2 File: GenLinkedList.c
3
4 Contains: Linked List utility routines
5
6 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
7 ("Apple") in consideration of your agreement to the following terms, and your
8 use, installation, modification or redistribution of this Apple software
9 constitutes acceptance of these terms. If you do not agree with these terms,
10 please do not use, install, modify or redistribute this Apple software.
11
12 In consideration of your agreement to abide by the following terms, and subject
13 to these terms, Apple grants you a personal, non-exclusive license, under Apple�s
14 copyrights in this original Apple software (the "Apple Software"), to use,
15 reproduce, modify and redistribute the Apple Software, with or without
16 modifications, in source and/or binary forms; provided that if you redistribute
17 the Apple Software in its entirety and without modifications, you must retain
18 this notice and the following text and disclaimers in all such redistributions of
19 the Apple Software. Neither the name, trademarks, service marks or logos of
20 Apple Computer, Inc. may be used to endorse or promote products derived from the
21 Apple Software without specific prior written permission from Apple. Except as
22 expressly stated in this notice, no other rights or licenses, express or implied,
23 are granted by Apple herein, including but not limited to any patent rights that
24 may be infringed by your derivative works or by other works in which the Apple
25 Software may be incorporated.
26
27 The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
28 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
29 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
31 COMBINATION WITH YOUR PRODUCTS.
32
33 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
34 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
35 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
37 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
38 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
39 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
41 Copyright � 2003-2004 Apple Computer, Inc., All Rights Reserved
42*/
43
44#include "GenLinkedList.h"
45
46#pragma mark --- Data Structures ---
47
48 /* This is the internal data structure for the nodes in the linked list. */
49 /* */
50 /* Note: The memory pointed to by pNext is owned by the list and is disposed of */
51 /* in DestroyList. It should not be disposed of in any other way. */
52 /* */
53 /* Note: The memory pointed to by pData is owned by the caller of the linked */
54 /* list. The caller is responsible for disposing of this memory. This can be */
55 /* done by simply implementing a DisposeDataProc that will be called on each */
56 /* node in the list, giving the caller a chance to dispose of any memory */
57 /* created. The DisposeDataProc is called from DestroyList */
58struct GenNode
59{
60 struct GenNode *pNext; /* Pointer to the next node in the list */
61 GenDataPtr pData; /* The data for this node, owned by caller */
62};
63typedef struct GenNode GenNode;
64
65#pragma mark --- List Implementation ---
66
67 /* Initializes the given GenLinkedList to an empty list. This MUST be */
68 /* called before any operations are performed on the list, otherwise bad things */
69 /* will happen. */
70void InitLinkedList( GenLinkedList *pList, DisposeDataProcPtr disposeProcPtr)
71{
72 if( pList == NULL )
73 return;
74
75 pList->pHead = pList->pTail = NULL;
76 pList->NumberOfItems = 0;
77 pList->DisposeProcPtr = disposeProcPtr;
78}
79
80 /* returns the current number of items in the given list. */
81 /* If pList == NULL, it returns 0 */
82ItemCount GetNumberOfItems( GenLinkedList *pList )
83{
84 return (pList) ? pList->NumberOfItems : 0;
85}
86
87 /* Creates a new node, containing pData, and adds it to the tail of pList. */
88 /* Note: if an error occurs, pList is unchanged. */
89OSErr AddToTail( GenLinkedList *pList, void *pData )
90{
91 OSErr err = paramErr;
92 GenNode *tmpNode = NULL;
93
94 if( pList == NULL || pData == NULL )
95 return err;
96
97 /* create memory for new node, if this fails we _must_ bail */
98 err = ( ( tmpNode = (GenNode*) NewPtr( sizeof( GenNode ) ) ) != NULL ) ? noErr : MemError();
99 if( err == noErr )
100 {
101 tmpNode->pData = pData; /* Setup new node */
102 tmpNode->pNext = NULL;
103
104 if( pList->pTail != NULL ) /* more then one item already */
105 ((GenNode*) pList->pTail)->pNext = (void*) tmpNode; /* so append to tail */
106 else
107 pList->pHead = (void*) tmpNode; /* no items, so adjust head */
108
109 pList->pTail = (void*) tmpNode;
110 pList->NumberOfItems += 1;
111 }
112
113 return err;
114}
115
116 /* Takes pSrcList and inserts it into pDestList at the location pIter points to. */
117 /* The lists must have the same DisposeProcPtr, but the Data can be different. If pSrcList */
118 /* is empty, it does nothing and just returns */
119 /* */
120 /* If pIter == NULL, insert pSrcList before the head */
121 /* else If pIter == pTail, append pSrcList to the tail */
122 /* else insert pSrcList in the middle somewhere */
123 /* On return: pSrcList is cleared and is an empty list. */
124 /* The data that was owned by pSrcList is now owned by pDestList */
125void InsertList( GenLinkedList *pDestList, GenLinkedList *pSrcList, GenIteratorPtr pIter )
126{
127 if( pDestList == NULL || pSrcList == NULL ||
128 pSrcList->pHead == NULL || pSrcList->pTail == NULL ||
129 pDestList->DisposeProcPtr != pSrcList->DisposeProcPtr )
130 return;
131
132 if( pDestList->pHead == NULL && pDestList->pTail == NULL ) /* empty list */
133 {
134 pDestList->pHead = pSrcList->pHead;
135 pDestList->pTail = pSrcList->pTail;
136 }
137 else if( pIter == NULL ) /* insert before head */
138 {
139 /* attach the list */
140 ((GenNode*)pSrcList->pTail)->pNext = pDestList->pHead;
141 /* fix up head */
142 pDestList->pHead = pSrcList->pHead;
143 }
144 else if( pIter == pDestList->pTail ) /* append to tail */
145 {
146 /* attach the list */
147 ((GenNode*)pDestList->pTail)->pNext = pSrcList->pHead;
148 /* fix up tail */
149 pDestList->pTail = pSrcList->pTail;
150 }
151 else /* insert in middle somewhere */
152 {
153 GenNode *tmpNode = ((GenNode*)pIter)->pNext;
154 ((GenNode*)pIter)->pNext = pSrcList->pHead;
155 ((GenNode*)pSrcList->pTail)->pNext = tmpNode;
156 }
157
158 pDestList->NumberOfItems += pSrcList->NumberOfItems; /* sync up NumberOfItems */
159
160 InitLinkedList( pSrcList, NULL); /* reset the source list */
161}
162
163 /* Goes through the list and disposes of any memory we allocated. Calls the DisposeProcPtr,*/
164 /* if it exists, to give the caller a chance to free up their memory */
165void DestroyList( GenLinkedList *pList )
166{
167 GenIteratorPtr pIter = NULL,
168 pNextIter = NULL;
169
170 if( pList == NULL )
171 return;
172
173 for( InitIterator( pList, &pIter ), pNextIter = pIter; pIter != NULL; pIter = pNextIter )
174 {
175 Next( &pNextIter ); /* get the next node before we blow away the link */
176
177 if( pList->DisposeProcPtr != NULL )
178 CallDisposeDataProc( pList->DisposeProcPtr, GetData( pIter ) );
179 DisposePtr( (char*) pIter );
180 }
181
182 InitLinkedList( pList, NULL);
183}
184
185/*#############################################*/
186/*#############################################*/
187/*#############################################*/
188
189#pragma mark -
190#pragma mark --- Iterator Implementation ---
191
192 /* Initializes pIter to point at the head of pList */
193 /* This must be called before performing any operations with pIter */
194void InitIterator( GenLinkedList *pList, GenIteratorPtr *pIter )
195{
196 if( pList != NULL && pIter != NULL )
197 *pIter = pList->pHead;
198}
199
200 /* On return, pIter points to the next node in the list. NULL if its gone */
201 /* past the end of the list */
202void Next( GenIteratorPtr *pIter )
203{
204 if( pIter != NULL )
205 *pIter = ((GenNode*)*pIter)->pNext;
206}
207
208 /* Returns the data of the current node that pIter points to */
209GenDataPtr GetData( GenIteratorPtr pIter )
210{
211 return ( pIter != NULL ) ? ((GenNode*)pIter)->pData : NULL;
212} \ No newline at end of file
diff --git a/linden/indra/mac_updater/GenLinkedList.h b/linden/indra/mac_updater/GenLinkedList.h
new file mode 100644
index 0000000..b5aa572
--- /dev/null
+++ b/linden/indra/mac_updater/GenLinkedList.h
@@ -0,0 +1,120 @@
1/**
2 * @file GenLinkedList.h
3 * @brief Mac OSX updater program
4 *
5 * Copyright (c) 2004-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28/*
29 File: GenLinkedList.h
30
31 Contains: Linked List utility routines prototypes
32
33 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
34 ("Apple") in consideration of your agreement to the following terms, and your
35 use, installation, modification or redistribution of this Apple software
36 constitutes acceptance of these terms. If you do not agree with these terms,
37 please do not use, install, modify or redistribute this Apple software.
38
39 In consideration of your agreement to abide by the following terms, and subject
40 to these terms, Apple grants you a personal, non-exclusive license, under Apple�s
41 copyrights in this original Apple software (the "Apple Software"), to use,
42 reproduce, modify and redistribute the Apple Software, with or without
43 modifications, in source and/or binary forms; provided that if you redistribute
44 the Apple Software in its entirety and without modifications, you must retain
45 this notice and the following text and disclaimers in all such redistributions of
46 the Apple Software. Neither the name, trademarks, service marks or logos of
47 Apple Computer, Inc. may be used to endorse or promote products derived from the
48 Apple Software without specific prior written permission from Apple. Except as
49 expressly stated in this notice, no other rights or licenses, express or implied,
50 are granted by Apple herein, including but not limited to any patent rights that
51 may be infringed by your derivative works or by other works in which the Apple
52 Software may be incorporated.
53
54 The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
55 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
56 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
58 COMBINATION WITH YOUR PRODUCTS.
59
60 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
61 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
62 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
63 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
64 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
65 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
66 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
67
68 Copyright � 2003-2004 Apple Computer, Inc., All Rights Reserved
69*/
70
71
72#ifndef __GENLINKEDLIST__
73#define __GENLINKEDLIST__
74
75#ifdef __cplusplus
76extern "C" {
77#endif
78
79#if TARGET_API_MAC_OSX || defined( __APPLE_CC__ )
80#include <CoreServices/CoreServices.h>
81#endif
82
83/* This is a quick, simple and generic linked list implementation. I tried */
84/* to setup the code so that you could use any linked list implementation you */
85/* want. They just need to support these few functions. */
86
87typedef void* GenIteratorPtr;
88typedef void* GenDataPtr;
89
90 /* This is a callback that is called from DestroyList for each node in the */
91 /* list. It gives the caller the oportunity to free any memory they might */
92 /* allocated in each node. */
93typedef CALLBACK_API( void , DisposeDataProcPtr ) ( GenDataPtr pData );
94
95#define CallDisposeDataProc( userRoutine, pData ) (*(userRoutine))((pData))
96
97struct GenLinkedList
98{
99 GenDataPtr pHead; /* Pointer to the head of the list */
100 GenDataPtr pTail; /* Pointer to the tail of the list */
101 ItemCount NumberOfItems; /* Number of items in the list (mostly for debugging) */
102 DisposeDataProcPtr DisposeProcPtr; /* rountine called to dispose of caller data, can be NULL */
103};
104typedef struct GenLinkedList GenLinkedList;
105
106void InitLinkedList ( GenLinkedList *pList, DisposeDataProcPtr disposeProcPtr );
107ItemCount GetNumberOfItems( GenLinkedList *pList );
108OSErr AddToTail ( GenLinkedList *pList, GenDataPtr pData );
109void InsertList ( GenLinkedList *pDestList, GenLinkedList *pSrcList, GenIteratorPtr pIter );
110void DestroyList ( GenLinkedList *pList );
111
112void InitIterator ( GenLinkedList *pList, GenIteratorPtr *pIter );
113void Next ( GenIteratorPtr *pIter );
114GenDataPtr GetData ( GenIteratorPtr pIter );
115
116#ifdef __cplusplus
117}
118#endif
119
120#endif
diff --git a/linden/indra/mac_updater/MoreFilesX.c b/linden/indra/mac_updater/MoreFilesX.c
new file mode 100644
index 0000000..33d3424
--- /dev/null
+++ b/linden/indra/mac_updater/MoreFilesX.c
@@ -0,0 +1,2765 @@
1/*
2 File: MoreFilesX.c
3
4 Contains: A collection of useful high-level File Manager routines
5 which use the HFS Plus APIs wherever possible.
6
7 Version: MoreFilesX 1.0.1
8
9 Copyright: � 1992-2002 by Apple Computer, Inc., all rights reserved.
10
11 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
12 ("Apple") in consideration of your agreement to the following terms, and your
13 use, installation, modification or redistribution of this Apple software
14 constitutes acceptance of these terms. If you do not agree with these terms,
15 please do not use, install, modify or redistribute this Apple software.
16
17 In consideration of your agreement to abide by the following terms, and subject
18 to these terms, Apple grants you a personal, non-exclusive license, under Apple�s
19 copyrights in this original Apple software (the "Apple Software"), to use,
20 reproduce, modify and redistribute the Apple Software, with or without
21 modifications, in source and/or binary forms; provided that if you redistribute
22 the Apple Software in its entirety and without modifications, you must retain
23 this notice and the following text and disclaimers in all such redistributions of
24 the Apple Software. Neither the name, trademarks, service marks or logos of
25 Apple Computer, Inc. may be used to endorse or promote products derived from the
26 Apple Software without specific prior written permission from Apple. Except as
27 expressly stated in this notice, no other rights or licenses, express or implied,
28 are granted by Apple herein, including but not limited to any patent rights that
29 may be infringed by your derivative works or by other works in which the Apple
30 Software may be incorporated.
31
32 The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
33 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
34 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
36 COMBINATION WITH YOUR PRODUCTS.
37
38 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
39 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
40 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
42 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
43 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
44 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45
46 File Ownership:
47
48 DRI: Apple Macintosh Developer Technical Support
49
50 Other Contact: For bug reports, consult the following page on
51 the World Wide Web:
52 http://developer.apple.com/bugreporter/
53
54 Technology: DTS Sample Code
55
56 Writers:
57
58 (JL) Jim Luther
59
60 Change History (most recent first):
61
62 <4> 8/22/02 JL [3016251] Changed FSMoveRenameObjectUnicode to not use
63 the Temporary folder because it isn't available on
64 NFS volumes.
65 <3> 4/19/02 JL [2853905] Fixed #if test around header includes.
66 <2> 4/19/02 JL [2850624] Fixed C++ compile errors and Project Builder
67 warnings.
68 <2> 4/19/02 JL [2853901] Updated standard disclaimer.
69 <1> 1/25/02 JL MoreFilesX 1.0
70*/
71
72#include <Carbon/Carbon.h>
73#include <string.h>
74
75#include "MoreFilesX.h"
76
77/* Set BuildingMoreFilesXForMacOS9 to 1 if building for Mac OS 9 */
78#ifndef BuildingMoreFilesXForMacOS9
79 #define BuildingMoreFilesXForMacOS9 0
80#endif
81
82/*****************************************************************************/
83
84#pragma mark ----- Local type definitions -----
85
86struct FSIterateContainerGlobals
87{
88 IterateContainerFilterProcPtr iterateFilter; /* pointer to IterateFilterProc */
89 FSCatalogInfoBitmap whichInfo; /* fields of the CatalogInfo to get */
90 FSCatalogInfo catalogInfo; /* FSCatalogInfo */
91 FSRef ref; /* FSRef */
92 FSSpec spec; /* FSSpec */
93 FSSpec *specPtr; /* pointer to spec field, or NULL */
94 HFSUniStr255 name; /* HFSUniStr255 */
95 HFSUniStr255 *namePtr; /* pointer to name field, or NULL */
96 void *yourDataPtr; /* a pointer to caller supplied data the filter may need to access */
97 ItemCount maxLevels; /* maximum levels to iterate through */
98 ItemCount currentLevel; /* the current level FSIterateContainerLevel is on */
99 Boolean quitFlag; /* set to true if filter wants to kill interation */
100 Boolean containerChanged; /* temporary - set to true if the current container changed during iteration */
101 OSErr result; /* result */
102 ItemCount actualObjects; /* number of objects returned */
103};
104typedef struct FSIterateContainerGlobals FSIterateContainerGlobals;
105
106struct FSDeleteContainerGlobals
107{
108 OSErr result; /* result */
109 ItemCount actualObjects; /* number of objects returned */
110 FSCatalogInfo catalogInfo; /* FSCatalogInfo */
111};
112typedef struct FSDeleteContainerGlobals FSDeleteContainerGlobals;
113
114/*****************************************************************************/
115
116#pragma mark ----- Local prototypes -----
117
118static
119void
120FSDeleteContainerLevel(
121 const FSRef *container,
122 FSDeleteContainerGlobals *theGlobals);
123
124static
125void
126FSIterateContainerLevel(
127 FSIterateContainerGlobals *theGlobals);
128
129static
130OSErr
131GenerateUniqueHFSUniStr(
132 long *startSeed,
133 const FSRef *dir1,
134 const FSRef *dir2,
135 HFSUniStr255 *uniqueName);
136
137/*****************************************************************************/
138
139#pragma mark ----- File Access Routines -----
140
141/*****************************************************************************/
142
143OSErr
144FSCopyFork(
145 SInt16 srcRefNum,
146 SInt16 dstRefNum,
147 void *copyBufferPtr,
148 ByteCount copyBufferSize)
149{
150 OSErr srcResult;
151 OSErr dstResult;
152 OSErr result;
153 SInt64 forkSize;
154 ByteCount readActualCount;
155
156 /* check input parameters */
157 require_action((NULL != copyBufferPtr) && (0 != copyBufferSize), BadParameter, result = paramErr);
158
159 /* get source fork size */
160 result = FSGetForkSize(srcRefNum, &forkSize);
161 require_noerr(result, SourceFSGetForkSizeFailed);
162
163 /* allocate disk space for destination fork */
164 result = FSSetForkSize(dstRefNum, fsFromStart, forkSize);
165 require_noerr(result, DestinationFSSetForkSizeFailed);
166
167 /* reset source fork's position to 0 */
168 result = FSSetForkPosition(srcRefNum, fsFromStart, 0);
169 require_noerr(result, SourceFSSetForkPositionFailed);
170
171 /* reset destination fork's position to 0 */
172 result = FSSetForkPosition(dstRefNum, fsFromStart, 0);
173 require_noerr(result, DestinationFSSetForkPositionFailed);
174
175 /* If copyBufferSize is greater than 4K bytes, make it a multiple of 4k bytes */
176 /* This will make writes on local volumes faster */
177 if ( (copyBufferSize >= 0x00001000) && ((copyBufferSize & 0x00000fff) != 0) )
178 {
179 copyBufferSize &= ~(0x00001000 - 1);
180 }
181
182 /* copy source to destination */
183 srcResult = dstResult = noErr;
184 while ( (noErr == srcResult) && (noErr == dstResult) )
185 {
186 srcResult = FSReadFork(srcRefNum, fsAtMark + noCacheMask, 0, copyBufferSize, copyBufferPtr, &readActualCount);
187 dstResult = FSWriteFork(dstRefNum, fsAtMark + noCacheMask, 0, readActualCount, copyBufferPtr, NULL);
188 }
189
190 /* make sure there were no errors at the destination */
191 require_noerr_action(dstResult, DestinationFSWriteForkFailed, result = dstResult);
192
193 /* make sure the error at the source was eofErr */
194 require_action(eofErr == srcResult, SourceResultNotEofErr, result = srcResult);
195
196 /* everything went as expected */
197 result = noErr;
198
199SourceResultNotEofErr:
200DestinationFSWriteForkFailed:
201DestinationFSSetForkPositionFailed:
202SourceFSSetForkPositionFailed:
203DestinationFSSetForkSizeFailed:
204SourceFSGetForkSizeFailed:
205BadParameter:
206
207 return ( result );
208}
209
210/*****************************************************************************/
211
212#pragma mark ----- Volume Access Routines -----
213
214/*****************************************************************************/
215
216OSErr
217FSGetVolParms(
218 FSVolumeRefNum volRefNum,
219 UInt32 bufferSize,
220 GetVolParmsInfoBuffer *volParmsInfo,
221 UInt32 *actualInfoSize)
222{
223 OSErr result;
224 HParamBlockRec pb;
225
226 /* check parameters */
227 require_action((NULL != volParmsInfo) && (NULL != actualInfoSize),
228 BadParameter, result = paramErr);
229
230 pb.ioParam.ioNamePtr = NULL;
231 pb.ioParam.ioVRefNum = volRefNum;
232 pb.ioParam.ioBuffer = (Ptr)volParmsInfo;
233 pb.ioParam.ioReqCount = (SInt32)bufferSize;
234 result = PBHGetVolParmsSync(&pb);
235 require_noerr(result, PBHGetVolParmsSync);
236
237 /* return number of bytes the file system returned in volParmsInfo buffer */
238 *actualInfoSize = (UInt32)pb.ioParam.ioActCount;
239
240PBHGetVolParmsSync:
241BadParameter:
242
243 return ( result );
244}
245
246/*****************************************************************************/
247
248OSErr
249FSGetVRefNum(
250 const FSRef *ref,
251 FSVolumeRefNum *vRefNum)
252{
253 OSErr result;
254 FSCatalogInfo catalogInfo;
255
256 /* check parameters */
257 require_action(NULL != vRefNum, BadParameter, result = paramErr);
258
259 /* get the volume refNum from the FSRef */
260 result = FSGetCatalogInfo(ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
261 require_noerr(result, FSGetCatalogInfo);
262
263 /* return volume refNum from catalogInfo */
264 *vRefNum = catalogInfo.volume;
265
266FSGetCatalogInfo:
267BadParameter:
268
269 return ( result );
270}
271
272/*****************************************************************************/
273
274OSErr
275FSGetVInfo(
276 FSVolumeRefNum volume,
277 HFSUniStr255 *volumeName, /* can be NULL */
278 UInt64 *freeBytes, /* can be NULL */
279 UInt64 *totalBytes) /* can be NULL */
280{
281 OSErr result;
282 FSVolumeInfo info;
283
284 /* ask for the volume's sizes only if needed */
285 result = FSGetVolumeInfo(volume, 0, NULL,
286 (((NULL != freeBytes) || (NULL != totalBytes)) ? kFSVolInfoSizes : kFSVolInfoNone),
287 &info, volumeName, NULL);
288 require_noerr(result, FSGetVolumeInfo);
289
290 if ( NULL != freeBytes )
291 {
292 *freeBytes = info.freeBytes;
293 }
294 if ( NULL != totalBytes )
295 {
296 *totalBytes = info.totalBytes;
297 }
298
299FSGetVolumeInfo:
300
301 return ( result );
302}
303
304/*****************************************************************************/
305
306OSErr
307FSGetVolFileSystemID(
308 FSVolumeRefNum volume,
309 UInt16 *fileSystemID, /* can be NULL */
310 UInt16 *signature) /* can be NULL */
311{
312 OSErr result;
313 FSVolumeInfo info;
314
315 result = FSGetVolumeInfo(volume, 0, NULL, kFSVolInfoFSInfo, &info, NULL, NULL);
316 require_noerr(result, FSGetVolumeInfo);
317
318 if ( NULL != fileSystemID )
319 {
320 *fileSystemID = info.filesystemID;
321 }
322 if ( NULL != signature )
323 {
324 *signature = info.signature;
325 }
326
327FSGetVolumeInfo:
328
329 return ( result );
330}
331
332/*****************************************************************************/
333
334OSErr
335FSGetMountedVolumes(
336 FSRef ***volumeRefsHandle, /* pointer to handle of FSRefs */
337 ItemCount *numVolumes)
338{
339 OSErr result;
340 OSErr memResult;
341 ItemCount volumeIndex;
342 FSRef ref;
343
344 /* check parameters */
345 require_action((NULL != volumeRefsHandle) && (NULL != numVolumes),
346 BadParameter, result = paramErr);
347
348 /* No volumes yet */
349 *numVolumes = 0;
350
351 /* Allocate a handle for the results */
352 *volumeRefsHandle = (FSRef **)NewHandle(0);
353 require_action(NULL != *volumeRefsHandle, NewHandle, result = memFullErr);
354
355 /* Call FSGetVolumeInfo in loop to get all volumes starting with the first */
356 volumeIndex = 1;
357 do
358 {
359 result = FSGetVolumeInfo(0, volumeIndex, NULL, kFSVolInfoNone, NULL, NULL, &ref);
360 if ( noErr == result )
361 {
362 /* concatenate the FSRef to the end of the handle */
363 PtrAndHand(&ref, (Handle)*volumeRefsHandle, sizeof(FSRef));
364 memResult = MemError();
365 require_noerr_action(memResult, MemoryAllocationFailed, result = memResult);
366
367 ++(*numVolumes); /* increment the volume count */
368 ++volumeIndex; /* and the volumeIndex to get the next volume*/
369 }
370 } while ( noErr == result );
371
372 /* nsvErr is OK -- it just means there are no more volumes */
373 require(nsvErr == result, FSGetVolumeInfo);
374
375 return ( noErr );
376
377 /**********************/
378
379MemoryAllocationFailed:
380FSGetVolumeInfo:
381
382 /* dispose of handle if already allocated and clear the outputs */
383 if ( NULL != *volumeRefsHandle )
384 {
385 DisposeHandle((Handle)*volumeRefsHandle);
386 *volumeRefsHandle = NULL;
387 }
388 *numVolumes = 0;
389
390NewHandle:
391BadParameter:
392
393 return ( result );
394}
395
396/*****************************************************************************/
397
398#pragma mark ----- FSRef/FSpec/Path/Name Conversion Routines -----
399
400/*****************************************************************************/
401
402OSErr
403FSRefMakeFSSpec(
404 const FSRef *ref,
405 FSSpec *spec)
406{
407 OSErr result;
408
409 /* check parameters */
410 require_action(NULL != spec, BadParameter, result = paramErr);
411
412 result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, spec, NULL);
413 require_noerr(result, FSGetCatalogInfo);
414
415FSGetCatalogInfo:
416BadParameter:
417
418 return ( result );
419}
420
421/*****************************************************************************/
422
423OSErr
424FSMakeFSRef(
425 FSVolumeRefNum volRefNum,
426 SInt32 dirID,
427 ConstStr255Param name,
428 FSRef *ref)
429{
430 OSErr result;
431 FSRefParam pb;
432
433 /* check parameters */
434 require_action(NULL != ref, BadParameter, result = paramErr);
435
436 pb.ioVRefNum = volRefNum;
437 pb.ioDirID = dirID;
438 pb.ioNamePtr = (StringPtr)name;
439 pb.newRef = ref;
440 result = PBMakeFSRefSync(&pb);
441 require_noerr(result, PBMakeFSRefSync);
442
443PBMakeFSRefSync:
444BadParameter:
445
446 return ( result );
447}
448
449/*****************************************************************************/
450
451OSStatus
452FSMakePath(
453 SInt16 volRefNum,
454 SInt32 dirID,
455 ConstStr255Param name,
456 UInt8 *path,
457 UInt32 maxPathSize)
458{
459 OSStatus result;
460 FSRef ref;
461
462 /* check parameters */
463 require_action(NULL != path, BadParameter, result = paramErr);
464
465 /* convert the inputs to an FSRef */
466 result = FSMakeFSRef(volRefNum, dirID, name, &ref);
467 require_noerr(result, FSMakeFSRef);
468
469 /* and then convert the FSRef to a path */
470 result = FSRefMakePath(&ref, path, maxPathSize);
471 require_noerr(result, FSRefMakePath);
472
473FSRefMakePath:
474FSMakeFSRef:
475BadParameter:
476
477 return ( result );
478}
479
480/*****************************************************************************/
481
482OSStatus
483FSPathMakeFSSpec(
484 const UInt8 *path,
485 FSSpec *spec,
486 Boolean *isDirectory) /* can be NULL */
487{
488 OSStatus result;
489 FSRef ref;
490
491 /* check parameters */
492 require_action(NULL != spec, BadParameter, result = paramErr);
493
494 /* convert the POSIX path to an FSRef */
495 result = FSPathMakeRef(path, &ref, isDirectory);
496 require_noerr(result, FSPathMakeRef);
497
498 /* and then convert the FSRef to an FSSpec */
499 result = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL);
500 require_noerr(result, FSGetCatalogInfo);
501
502FSGetCatalogInfo:
503FSPathMakeRef:
504BadParameter:
505
506 return ( result );
507}
508
509/*****************************************************************************/
510
511OSErr
512UnicodeNameGetHFSName(
513 UniCharCount nameLength,
514 const UniChar *name,
515 TextEncoding textEncodingHint,
516 Boolean isVolumeName,
517 Str31 hfsName)
518{
519 OSStatus result;
520 ByteCount unicodeByteLength;
521 ByteCount unicodeBytesConverted;
522 ByteCount actualPascalBytes;
523 UnicodeMapping uMapping;
524 UnicodeToTextInfo utInfo;
525
526 /* check parameters */
527 require_action(NULL != hfsName, BadParameter, result = paramErr);
528
529 /* make sure output is valid in case we get errors or there's nothing to convert */
530 hfsName[0] = 0;
531
532 unicodeByteLength = nameLength * sizeof(UniChar);
533 if ( 0 == unicodeByteLength )
534 {
535 /* do nothing */
536 result = noErr;
537 }
538 else
539 {
540 /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */
541 if ( kTextEncodingUnknown == textEncodingHint )
542 {
543 ScriptCode script;
544 RegionCode region;
545
546 script = (ScriptCode)GetScriptManagerVariable(smSysScript);
547 region = (RegionCode)GetScriptManagerVariable(smRegionCode);
548 result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region,
549 NULL, &textEncodingHint );
550 if ( paramErr == result )
551 {
552 /* ok, ignore the region and try again */
553 result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare,
554 kTextRegionDontCare, NULL, &textEncodingHint );
555 }
556 if ( noErr != result )
557 {
558 /* ok... try something */
559 textEncodingHint = kTextEncodingMacRoman;
560 }
561 }
562
563 uMapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeV2_0,
564 kUnicodeCanonicalDecompVariant, kUnicode16BitFormat);
565 uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint);
566 uMapping.mappingVersion = kUnicodeUseHFSPlusMapping;
567
568 result = CreateUnicodeToTextInfo(&uMapping, &utInfo);
569 require_noerr(result, CreateUnicodeToTextInfo);
570
571 result = ConvertFromUnicodeToText(utInfo, unicodeByteLength, name, kUnicodeLooseMappingsMask,
572 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */
573 isVolumeName ? kHFSMaxVolumeNameChars : kHFSMaxFileNameChars,
574 &unicodeBytesConverted, &actualPascalBytes, &hfsName[1]);
575 require_noerr(result, ConvertFromUnicodeToText);
576
577 hfsName[0] = (unsigned char)actualPascalBytes; /* fill in length byte */
578
579ConvertFromUnicodeToText:
580
581 /* verify the result in debug builds -- there's really not anything you can do if it fails */
582 verify_noerr(DisposeUnicodeToTextInfo(&utInfo));
583 }
584
585CreateUnicodeToTextInfo:
586BadParameter:
587
588 return ( result );
589}
590
591/*****************************************************************************/
592
593OSErr
594HFSNameGetUnicodeName(
595 ConstStr31Param hfsName,
596 TextEncoding textEncodingHint,
597 HFSUniStr255 *unicodeName)
598{
599 ByteCount unicodeByteLength;
600 OSStatus result;
601 UnicodeMapping uMapping;
602 TextToUnicodeInfo tuInfo;
603 ByteCount pascalCharsRead;
604
605 /* check parameters */
606 require_action(NULL != unicodeName, BadParameter, result = paramErr);
607
608 /* make sure output is valid in case we get errors or there's nothing to convert */
609 unicodeName->length = 0;
610
611 if ( 0 == StrLength(hfsName) )
612 {
613 result = noErr;
614 }
615 else
616 {
617 /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */
618 if ( kTextEncodingUnknown == textEncodingHint )
619 {
620 ScriptCode script;
621 RegionCode region;
622
623 script = GetScriptManagerVariable(smSysScript);
624 region = GetScriptManagerVariable(smRegionCode);
625 result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region,
626 NULL, &textEncodingHint);
627 if ( paramErr == result )
628 {
629 /* ok, ignore the region and try again */
630 result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare,
631 kTextRegionDontCare, NULL, &textEncodingHint);
632 }
633 if ( noErr != result )
634 {
635 /* ok... try something */
636 textEncodingHint = kTextEncodingMacRoman;
637 }
638 }
639
640 uMapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeV2_0,
641 kUnicodeCanonicalDecompVariant, kUnicode16BitFormat);
642 uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint);
643 uMapping.mappingVersion = kUnicodeUseHFSPlusMapping;
644
645 result = CreateTextToUnicodeInfo(&uMapping, &tuInfo);
646 require_noerr(result, CreateTextToUnicodeInfo);
647
648 result = ConvertFromTextToUnicode(tuInfo, hfsName[0], &hfsName[1],
649 0, /* no control flag bits */
650 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */
651 sizeof(unicodeName->unicode), /* output buffer size in bytes */
652 &pascalCharsRead, &unicodeByteLength, unicodeName->unicode);
653 require_noerr(result, ConvertFromTextToUnicode);
654
655 /* convert from byte count to char count */
656 unicodeName->length = unicodeByteLength / sizeof(UniChar);
657
658ConvertFromTextToUnicode:
659
660 /* verify the result in debug builds -- there's really not anything you can do if it fails */
661 verify_noerr(DisposeTextToUnicodeInfo(&tuInfo));
662 }
663
664CreateTextToUnicodeInfo:
665BadParameter:
666
667 return ( result );
668}
669
670/*****************************************************************************/
671
672#pragma mark ----- File/Directory Manipulation Routines -----
673
674/*****************************************************************************/
675
676Boolean FSRefValid(const FSRef *ref)
677{
678 return ( noErr == FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, NULL, NULL) );
679}
680
681/*****************************************************************************/
682
683OSErr
684FSGetParentRef(
685 const FSRef *ref,
686 FSRef *parentRef)
687{
688 OSErr result;
689 FSCatalogInfo catalogInfo;
690
691 /* check parameters */
692 require_action(NULL != parentRef, BadParameter, result = paramErr);
693
694 result = FSGetCatalogInfo(ref, kFSCatInfoNodeID, &catalogInfo, NULL, NULL, parentRef);
695 require_noerr(result, FSGetCatalogInfo);
696
697 /*
698 * Note: FSRefs always point to real file system objects. So, there cannot
699 * be a FSRef to the parent of volume root directories. Early versions of
700 * Mac OS X do not handle this case correctly and incorrectly return a
701 * FSRef for the parent of volume root directories instead of returning an
702 * invalid FSRef (a cleared FSRef is invalid). The next three lines of code
703 * ensure that you won't run into this bug. WW9D!
704 */
705 if ( fsRtDirID == catalogInfo.nodeID )
706 {
707 /* clear parentRef and return noErr which is the proper behavior */
708 memset(parentRef, 0, sizeof(FSRef));
709 }
710
711FSGetCatalogInfo:
712BadParameter:
713
714 return ( result );
715}
716
717/*****************************************************************************/
718
719OSErr
720FSGetFileDirName(
721 const FSRef *ref,
722 HFSUniStr255 *outName)
723{
724 OSErr result;
725
726 /* check parameters */
727 require_action(NULL != outName, BadParameter, result = paramErr);
728
729 result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, outName, NULL, NULL);
730 require_noerr(result, FSGetCatalogInfo);
731
732FSGetCatalogInfo:
733BadParameter:
734
735 return ( result );
736}
737
738/*****************************************************************************/
739
740OSErr
741FSGetNodeID(
742 const FSRef *ref,
743 long *nodeID, /* can be NULL */
744 Boolean *isDirectory) /* can be NULL */
745{
746 OSErr result;
747 FSCatalogInfo catalogInfo;
748 FSCatalogInfoBitmap whichInfo;
749
750 /* determine what catalog information to get */
751 whichInfo = kFSCatInfoNone; /* start with none */
752 if ( NULL != nodeID )
753 {
754 whichInfo |= kFSCatInfoNodeID;
755 }
756 if ( NULL != isDirectory )
757 {
758 whichInfo |= kFSCatInfoNodeFlags;
759 }
760
761 result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL);
762 require_noerr(result, FSGetCatalogInfo);
763
764 if ( NULL != nodeID )
765 {
766 *nodeID = catalogInfo.nodeID;
767 }
768 if ( NULL != isDirectory )
769 {
770 *isDirectory = (0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags));
771 }
772
773FSGetCatalogInfo:
774
775 return ( result );
776}
777
778/*****************************************************************************/
779
780OSErr
781FSGetUserPrivilegesPermissions(
782 const FSRef *ref,
783 UInt8 *userPrivileges, /* can be NULL */
784 UInt32 permissions[4]) /* can be NULL */
785{
786 OSErr result;
787 FSCatalogInfo catalogInfo;
788 FSCatalogInfoBitmap whichInfo;
789
790 /* determine what catalog information to get */
791 whichInfo = kFSCatInfoNone; /* start with none */
792 if ( NULL != userPrivileges )
793 {
794 whichInfo |= kFSCatInfoUserPrivs;
795 }
796 if ( NULL != permissions )
797 {
798 whichInfo |= kFSCatInfoPermissions;
799 }
800
801 result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL);
802 require_noerr(result, FSGetCatalogInfo);
803
804 if ( NULL != userPrivileges )
805 {
806 *userPrivileges = catalogInfo.userPrivileges;
807 }
808 if ( NULL != permissions )
809 {
810 BlockMoveData(&catalogInfo.permissions, permissions, sizeof(UInt32) * 4);
811 }
812
813FSGetCatalogInfo:
814
815 return ( result );
816}
817
818/*****************************************************************************/
819
820OSErr
821FSCheckLock(
822 const FSRef *ref)
823{
824 OSErr result;
825 FSCatalogInfo catalogInfo;
826 FSVolumeInfo volumeInfo;
827
828 /* get nodeFlags and vRefNum for container */
829 result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoVolume, &catalogInfo, NULL, NULL,NULL);
830 require_noerr(result, FSGetCatalogInfo);
831
832 /* is file locked? */
833 if ( 0 != (catalogInfo.nodeFlags & kFSNodeLockedMask) )
834 {
835 result = fLckdErr; /* file is locked */
836 }
837 else
838 {
839 /* file isn't locked, but is volume locked? */
840
841 /* get volume flags */
842 result = FSGetVolumeInfo(catalogInfo.volume, 0, NULL, kFSVolInfoFlags, &volumeInfo, NULL, NULL);
843 require_noerr(result, FSGetVolumeInfo);
844
845 if ( 0 != (volumeInfo.flags & kFSVolFlagHardwareLockedMask) )
846 {
847 result = wPrErr; /* volume locked by hardware */
848 }
849 else if ( 0 != (volumeInfo.flags & kFSVolFlagSoftwareLockedMask) )
850 {
851 result = vLckdErr; /* volume locked by software */
852 }
853 }
854
855FSGetVolumeInfo:
856FSGetCatalogInfo:
857
858 return ( result );
859}
860
861/*****************************************************************************/
862
863OSErr
864FSGetForkSizes(
865 const FSRef *ref,
866 UInt64 *dataLogicalSize, /* can be NULL */
867 UInt64 *rsrcLogicalSize) /* can be NULL */
868{
869 OSErr result;
870 FSCatalogInfoBitmap whichInfo;
871 FSCatalogInfo catalogInfo;
872
873 whichInfo = kFSCatInfoNodeFlags;
874 if ( NULL != dataLogicalSize )
875 {
876 /* get data fork size */
877 whichInfo |= kFSCatInfoDataSizes;
878 }
879 if ( NULL != rsrcLogicalSize )
880 {
881 /* get resource fork size */
882 whichInfo |= kFSCatInfoRsrcSizes;
883 }
884
885 /* get nodeFlags and catalog info */
886 result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL,NULL);
887 require_noerr(result, FSGetCatalogInfo);
888
889 /* make sure FSRef was to a file */
890 require_action(0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), FSRefNotFile, result = notAFileErr);
891
892 if ( NULL != dataLogicalSize )
893 {
894 /* return data fork size */
895 *dataLogicalSize = catalogInfo.dataLogicalSize;
896 }
897 if ( NULL != rsrcLogicalSize )
898 {
899 /* return resource fork size */
900 *rsrcLogicalSize = catalogInfo.rsrcLogicalSize;
901 }
902
903FSRefNotFile:
904FSGetCatalogInfo:
905
906 return ( result );
907}
908
909/*****************************************************************************/
910
911OSErr
912FSGetTotalForkSizes(
913 const FSRef *ref,
914 UInt64 *totalLogicalSize, /* can be NULL */
915 UInt64 *totalPhysicalSize, /* can be NULL */
916 ItemCount *forkCount) /* can be NULL */
917{
918 OSErr result;
919 CatPositionRec forkIterator;
920 SInt64 forkSize;
921 SInt64 *forkSizePtr;
922 UInt64 forkPhysicalSize;
923 UInt64 *forkPhysicalSizePtr;
924
925 /* Determine if forkSize needed */
926 if ( NULL != totalLogicalSize)
927 {
928 *totalLogicalSize = 0;
929 forkSizePtr = &forkSize;
930 }
931 else
932 {
933 forkSizePtr = NULL;
934 }
935
936 /* Determine if forkPhysicalSize is needed */
937 if ( NULL != totalPhysicalSize )
938 {
939 *totalPhysicalSize = 0;
940 forkPhysicalSizePtr = &forkPhysicalSize;
941 }
942 else
943 {
944 forkPhysicalSizePtr = NULL;
945 }
946
947 /* zero fork count if returning it */
948 if ( NULL != forkCount )
949 {
950 *forkCount = 0;
951 }
952
953 /* Iterate through the forks to get the sizes */
954 forkIterator.initialize = 0;
955 do
956 {
957 result = FSIterateForks(ref, &forkIterator, NULL, forkSizePtr, forkPhysicalSizePtr);
958 if ( noErr == result )
959 {
960 if ( NULL != totalLogicalSize )
961 {
962 *totalLogicalSize += forkSize;
963 }
964
965 if ( NULL != totalPhysicalSize )
966 {
967 *totalPhysicalSize += forkPhysicalSize;
968 }
969
970 if ( NULL != forkCount )
971 {
972 ++*forkCount;
973 }
974 }
975 } while ( noErr == result );
976
977 /* any error result other than errFSNoMoreItems is serious */
978 require(errFSNoMoreItems == result, FSIterateForks);
979
980 /* Normal exit */
981 result = noErr;
982
983FSIterateForks:
984
985 return ( result );
986}
987
988/*****************************************************************************/
989
990OSErr
991FSBumpDate(
992 const FSRef *ref)
993{
994 OSStatus result;
995 FSCatalogInfo catalogInfo;
996 UTCDateTime oldDateTime;
997#if !BuildingMoreFilesXForMacOS9
998 FSRef parentRef;
999 Boolean notifyParent;
1000#endif
1001
1002#if !BuildingMoreFilesXForMacOS9
1003 /* Get the node flags, the content modification date and time, and the parent ref */
1004 result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoContentMod, &catalogInfo, NULL, NULL, &parentRef);
1005 require_noerr(result, FSGetCatalogInfo);
1006
1007 /* Notify the parent if this is a file */
1008 notifyParent = (0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask));
1009#else
1010 /* Get the content modification date and time */
1011 result = FSGetCatalogInfo(ref, kFSCatInfoContentMod, &catalogInfo, NULL, NULL, NULL);
1012 require_noerr(result, FSGetCatalogInfo);
1013#endif
1014
1015 oldDateTime = catalogInfo.contentModDate;
1016
1017 /* Get the current date and time */
1018 result = GetUTCDateTime(&catalogInfo.contentModDate, kUTCDefaultOptions);
1019 require_noerr(result, GetUTCDateTime);
1020
1021 /* if the old date and time is the the same as the current, bump the seconds by one */
1022 if ( (catalogInfo.contentModDate.fraction == oldDateTime.fraction) &&
1023 (catalogInfo.contentModDate.lowSeconds == oldDateTime.lowSeconds) &&
1024 (catalogInfo.contentModDate.highSeconds == oldDateTime.highSeconds) )
1025 {
1026 ++catalogInfo.contentModDate.lowSeconds;
1027 if ( 0 == catalogInfo.contentModDate.lowSeconds )
1028 {
1029 ++catalogInfo.contentModDate.highSeconds;
1030 }
1031 }
1032
1033 /* Bump the content modification date and time */
1034 result = FSSetCatalogInfo(ref, kFSCatInfoContentMod, &catalogInfo);
1035 require_noerr(result, FSSetCatalogInfo);
1036
1037#if !BuildingMoreFilesXForMacOS9
1038 /*
1039 * The problem with FNNotify is that it is not available under Mac OS 9
1040 * and there's no way to test for that except for looking for the symbol
1041 * or something. So, I'll just conditionalize this for those who care
1042 * to send a notification.
1043 */
1044
1045 /* Send a notification for the parent of the file, or for the directory */
1046 result = FNNotify(notifyParent ? &parentRef : ref, kFNDirectoryModifiedMessage, kNilOptions);
1047 require_noerr(result, FNNotify);
1048#endif
1049
1050 /* ignore errors from FSSetCatalogInfo (volume might be write protected) and FNNotify */
1051FNNotify:
1052FSSetCatalogInfo:
1053
1054 return ( noErr );
1055
1056 /**********************/
1057
1058GetUTCDateTime:
1059FSGetCatalogInfo:
1060
1061 return ( result );
1062}
1063
1064/*****************************************************************************/
1065
1066OSErr
1067FSGetFinderInfo(
1068 const FSRef *ref,
1069 FinderInfo *info, /* can be NULL */
1070 ExtendedFinderInfo *extendedInfo, /* can be NULL */
1071 Boolean *isDirectory) /* can be NULL */
1072{
1073 OSErr result;
1074 FSCatalogInfo catalogInfo;
1075 FSCatalogInfoBitmap whichInfo;
1076
1077 /* determine what catalog information is really needed */
1078 whichInfo = kFSCatInfoNone;
1079
1080 if ( NULL != info )
1081 {
1082 /* get FinderInfo */
1083 whichInfo |= kFSCatInfoFinderInfo;
1084 }
1085
1086 if ( NULL != extendedInfo )
1087 {
1088 /* get ExtendedFinderInfo */
1089 whichInfo |= kFSCatInfoFinderXInfo;
1090 }
1091
1092 if ( NULL != isDirectory )
1093 {
1094 whichInfo |= kFSCatInfoNodeFlags;
1095 }
1096
1097 result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL);
1098 require_noerr(result, FSGetCatalogInfo);
1099
1100 /* return FinderInfo if requested */
1101 if ( NULL != info )
1102 {
1103 BlockMoveData(catalogInfo.finderInfo, info, sizeof(FinderInfo));
1104 }
1105
1106 /* return ExtendedFinderInfo if requested */
1107 if ( NULL != extendedInfo)
1108 {
1109 BlockMoveData(catalogInfo.extFinderInfo, extendedInfo, sizeof(ExtendedFinderInfo));
1110 }
1111
1112 /* set isDirectory Boolean if requested */
1113 if ( NULL != isDirectory)
1114 {
1115 *isDirectory = (0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags));
1116 }
1117
1118FSGetCatalogInfo:
1119
1120 return ( result );
1121}
1122
1123/*****************************************************************************/
1124
1125OSErr
1126FSSetFinderInfo(
1127 const FSRef *ref,
1128 const FinderInfo *info,
1129 const ExtendedFinderInfo *extendedInfo)
1130{
1131 OSErr result;
1132 FSCatalogInfo catalogInfo;
1133 FSCatalogInfoBitmap whichInfo;
1134
1135 /* determine what catalog information will be set */
1136 whichInfo = kFSCatInfoNone; /* start with none */
1137 if ( NULL != info )
1138 {
1139 /* set FinderInfo */
1140 whichInfo |= kFSCatInfoFinderInfo;
1141 BlockMoveData(info, catalogInfo.finderInfo, sizeof(FinderInfo));
1142 }
1143 if ( NULL != extendedInfo )
1144 {
1145 /* set ExtendedFinderInfo */
1146 whichInfo |= kFSCatInfoFinderXInfo;
1147 BlockMoveData(extendedInfo, catalogInfo.extFinderInfo, sizeof(ExtendedFinderInfo));
1148 }
1149
1150 result = FSSetCatalogInfo(ref, whichInfo, &catalogInfo);
1151 require_noerr(result, FSGetCatalogInfo);
1152
1153FSGetCatalogInfo:
1154
1155 return ( result );
1156}
1157
1158/*****************************************************************************/
1159
1160OSErr
1161FSChangeCreatorType(
1162 const FSRef *ref,
1163 OSType fileCreator,
1164 OSType fileType)
1165{
1166 OSErr result;
1167 FSCatalogInfo catalogInfo;
1168 FSRef parentRef;
1169
1170 /* get nodeFlags, finder info, and parent FSRef */
1171 result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoFinderInfo, &catalogInfo , NULL, NULL, &parentRef);
1172 require_noerr(result, FSGetCatalogInfo);
1173
1174 /* make sure FSRef was to a file */
1175 require_action(0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), FSRefNotFile, result = notAFileErr);
1176
1177 /* If fileType not 0x00000000, change fileType */
1178 if ( fileType != (OSType)0x00000000 )
1179 {
1180 ((FileInfo *)&catalogInfo.finderInfo)->fileType = fileType;
1181 }
1182
1183 /* If creator not 0x00000000, change creator */
1184 if ( fileCreator != (OSType)0x00000000 )
1185 {
1186 ((FileInfo *)&catalogInfo.finderInfo)->fileCreator = fileCreator;
1187 }
1188
1189 /* now, save the new information back to disk */
1190 result = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo);
1191 require_noerr(result, FSSetCatalogInfo);
1192
1193 /* and attempt to bump the parent directory's mod date to wake up */
1194 /* the Finder to the change we just made (ignore errors from this) */
1195 verify_noerr(FSBumpDate(&parentRef));
1196
1197FSSetCatalogInfo:
1198FSRefNotFile:
1199FSGetCatalogInfo:
1200
1201 return ( result );
1202}
1203
1204/*****************************************************************************/
1205
1206OSErr
1207FSChangeFinderFlags(
1208 const FSRef *ref,
1209 Boolean setBits,
1210 UInt16 flagBits)
1211{
1212 OSErr result;
1213 FSCatalogInfo catalogInfo;
1214 FSRef parentRef;
1215
1216 /* get the current finderInfo */
1217 result = FSGetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, &parentRef);
1218 require_noerr(result, FSGetCatalogInfo);
1219
1220 /* set or clear the appropriate bits in the finderInfo.finderFlags */
1221 if ( setBits )
1222 {
1223 /* OR in the bits */
1224 ((FileInfo *)&catalogInfo.finderInfo)->finderFlags |= flagBits;
1225 }
1226 else
1227 {
1228 /* AND out the bits */
1229 ((FileInfo *)&catalogInfo.finderInfo)->finderFlags &= ~flagBits;
1230 }
1231
1232 /* save the modified finderInfo */
1233 result = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo);
1234 require_noerr(result, FSSetCatalogInfo);
1235
1236 /* and attempt to bump the parent directory's mod date to wake up the Finder */
1237 /* to the change we just made (ignore errors from this) */
1238 verify_noerr(FSBumpDate(&parentRef));
1239
1240FSSetCatalogInfo:
1241FSGetCatalogInfo:
1242
1243 return ( result );
1244}
1245
1246/*****************************************************************************/
1247
1248OSErr
1249FSSetInvisible(
1250 const FSRef *ref)
1251{
1252 return ( FSChangeFinderFlags(ref, true, kIsInvisible) );
1253}
1254
1255OSErr
1256FSClearInvisible(
1257 const FSRef *ref)
1258{
1259 return ( FSChangeFinderFlags(ref, false, kIsInvisible) );
1260}
1261
1262/*****************************************************************************/
1263
1264OSErr
1265FSSetNameLocked(
1266 const FSRef *ref)
1267{
1268 return ( FSChangeFinderFlags(ref, true, kNameLocked) );
1269}
1270
1271OSErr
1272FSClearNameLocked(
1273 const FSRef *ref)
1274{
1275 return ( FSChangeFinderFlags(ref, false, kNameLocked) );
1276}
1277
1278/*****************************************************************************/
1279
1280OSErr
1281FSSetIsStationery(
1282 const FSRef *ref)
1283{
1284 return ( FSChangeFinderFlags(ref, true, kIsStationery) );
1285}
1286
1287OSErr
1288FSClearIsStationery(
1289 const FSRef *ref)
1290{
1291 return ( FSChangeFinderFlags(ref, false, kIsStationery) );
1292}
1293
1294/*****************************************************************************/
1295
1296OSErr
1297FSSetHasCustomIcon(
1298 const FSRef *ref)
1299{
1300 return ( FSChangeFinderFlags(ref, true, kHasCustomIcon) );
1301}
1302
1303OSErr
1304FSClearHasCustomIcon(
1305 const FSRef *ref)
1306{
1307 return ( FSChangeFinderFlags(ref, false, kHasCustomIcon) );
1308}
1309
1310/*****************************************************************************/
1311
1312OSErr
1313FSClearHasBeenInited(
1314 const FSRef *ref)
1315{
1316 return ( FSChangeFinderFlags(ref, false, kHasBeenInited) );
1317}
1318
1319/*****************************************************************************/
1320
1321OSErr
1322FSCopyFileMgrAttributes(
1323 const FSRef *sourceRef,
1324 const FSRef *destinationRef,
1325 Boolean copyLockBit)
1326{
1327 OSErr result;
1328 FSCatalogInfo catalogInfo;
1329
1330 /* get the source information */
1331 result = FSGetCatalogInfo(sourceRef, kFSCatInfoSettableInfo, &catalogInfo, NULL, NULL, NULL);
1332 require_noerr(result, FSGetCatalogInfo);
1333
1334 /* don't copy the hasBeenInited bit; clear it */
1335 ((FileInfo *)&catalogInfo.finderInfo)->finderFlags &= ~kHasBeenInited;
1336
1337 /* should the locked bit be copied? */
1338 if ( !copyLockBit )
1339 {
1340 /* no, make sure the locked bit is clear */
1341 catalogInfo.nodeFlags &= ~kFSNodeLockedMask;
1342 }
1343
1344 /* set the destination information */
1345 result = FSSetCatalogInfo(destinationRef, kFSCatInfoSettableInfo, &catalogInfo);
1346 require_noerr(result, FSSetCatalogInfo);
1347
1348FSSetCatalogInfo:
1349FSGetCatalogInfo:
1350
1351 return ( result );
1352}
1353
1354/*****************************************************************************/
1355
1356OSErr
1357FSMoveRenameObjectUnicode(
1358 const FSRef *ref,
1359 const FSRef *destDirectory,
1360 UniCharCount nameLength,
1361 const UniChar *name, /* can be NULL (no rename during move) */
1362 TextEncoding textEncodingHint,
1363 FSRef *newRef) /* if function fails along the way, newRef is final location of file */
1364{
1365 OSErr result;
1366 FSVolumeRefNum vRefNum;
1367 FSCatalogInfo catalogInfo;
1368 FSRef originalDirectory;
1369 TextEncoding originalTextEncodingHint;
1370 HFSUniStr255 originalName;
1371 HFSUniStr255 uniqueName; /* unique name given to object while moving it to destination */
1372 long theSeed; /* the seed for generating unique names */
1373
1374 /* check parameters */
1375 require_action(NULL != newRef, BadParameter, result = paramErr);
1376
1377 /* newRef = input to start with */
1378 BlockMoveData(ref, newRef, sizeof(FSRef));
1379
1380 /* get destDirectory's vRefNum */
1381 result = FSGetCatalogInfo(destDirectory, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
1382 require_noerr(result, DestinationBad);
1383
1384 /* save vRefNum */
1385 vRefNum = catalogInfo.volume;
1386
1387 /* get ref's vRefNum, TextEncoding, name and parent directory*/
1388 result = FSGetCatalogInfo(ref, kFSCatInfoTextEncoding + kFSCatInfoVolume, &catalogInfo, &originalName, NULL, &originalDirectory);
1389 require_noerr(result, SourceBad);
1390
1391 /* save TextEncoding */
1392 originalTextEncodingHint = catalogInfo.textEncodingHint;
1393
1394 /* make sure ref and destDirectory are on same volume */
1395 require_action(vRefNum == catalogInfo.volume, NotSameVolume, result = diffVolErr);
1396
1397 /* Skip a few steps if we're not renaming */
1398 if ( NULL != name )
1399 {
1400 /* generate a name that is unique in both directories */
1401 theSeed = 0x4a696d4c; /* a fine unlikely filename */
1402
1403 result = GenerateUniqueHFSUniStr(&theSeed, &originalDirectory, destDirectory, &uniqueName);
1404 require_noerr(result, GenerateUniqueHFSUniStrFailed);
1405
1406 /* Rename the object to uniqueName */
1407 result = FSRenameUnicode(ref, uniqueName.length, uniqueName.unicode, kTextEncodingUnknown, newRef);
1408 require_noerr(result, FSRenameUnicodeBeforeMoveFailed);
1409
1410 /* Move object to its new home */
1411 result = FSMoveObject(newRef, destDirectory, newRef);
1412 require_noerr(result, FSMoveObjectAfterRenameFailed);
1413
1414 /* Rename the object to new name */
1415 result = FSRenameUnicode(ref, nameLength, name, textEncodingHint, newRef);
1416 require_noerr(result, FSRenameUnicodeAfterMoveFailed);
1417 }
1418 else
1419 {
1420 /* Move object to its new home */
1421 result = FSMoveObject(newRef, destDirectory, newRef);
1422 require_noerr(result, FSMoveObjectNoRenameFailed);
1423 }
1424
1425 return ( result );
1426
1427 /*************/
1428
1429/*
1430 * failure handling code when renaming
1431 */
1432
1433FSRenameUnicodeAfterMoveFailed:
1434
1435 /* Error handling: move object back to original location - ignore errors */
1436 verify_noerr(FSMoveObject(newRef, &originalDirectory, newRef));
1437
1438FSMoveObjectAfterRenameFailed:
1439
1440 /* Error handling: rename object back to original name - ignore errors */
1441 verify_noerr(FSRenameUnicode(newRef, originalName.length, originalName.unicode, originalTextEncodingHint, newRef));
1442
1443FSRenameUnicodeBeforeMoveFailed:
1444GenerateUniqueHFSUniStrFailed:
1445
1446/*
1447 * failure handling code for renaming or not
1448 */
1449FSMoveObjectNoRenameFailed:
1450NotSameVolume:
1451SourceBad:
1452DestinationBad:
1453BadParameter:
1454
1455 return ( result );
1456}
1457
1458/*****************************************************************************/
1459
1460/*
1461 The FSDeleteContainerLevel function deletes the contents of a container
1462 directory. All files and subdirectories in the specified container are
1463 deleted. If a locked file or directory is encountered, it is unlocked
1464 and then deleted. If any unexpected errors are encountered,
1465 FSDeleteContainerLevel quits and returns to the caller.
1466
1467 container --> FSRef to a directory.
1468 theGlobals --> A pointer to a FSDeleteContainerGlobals struct
1469 which contains the variables that do not need to
1470 be allocated each time FSDeleteContainerLevel
1471 recurses. That lets FSDeleteContainerLevel use
1472 less stack space per recursion level.
1473*/
1474
1475static
1476void
1477FSDeleteContainerLevel(
1478 const FSRef *container,
1479 FSDeleteContainerGlobals *theGlobals)
1480{
1481 /* level locals */
1482 FSIterator iterator;
1483 FSRef itemToDelete;
1484 UInt16 nodeFlags;
1485
1486 /* Open FSIterator for flat access and give delete optimization hint */
1487 theGlobals->result = FSOpenIterator(container, kFSIterateFlat + kFSIterateDelete, &iterator);
1488 require_noerr(theGlobals->result, FSOpenIterator);
1489
1490 /* delete the contents of the directory */
1491 do
1492 {
1493 /* get 1 item to delete */
1494 theGlobals->result = FSGetCatalogInfoBulk(iterator, 1, &theGlobals->actualObjects,
1495 NULL, kFSCatInfoNodeFlags, &theGlobals->catalogInfo,
1496 &itemToDelete, NULL, NULL);
1497 if ( (noErr == theGlobals->result) && (1 == theGlobals->actualObjects) )
1498 {
1499 /* save node flags in local in case we have to recurse */
1500 nodeFlags = theGlobals->catalogInfo.nodeFlags;
1501
1502 /* is it a file or directory? */
1503 if ( 0 != (nodeFlags & kFSNodeIsDirectoryMask) )
1504 {
1505 /* it's a directory -- delete its contents before attempting to delete it */
1506 FSDeleteContainerLevel(&itemToDelete, theGlobals);
1507 }
1508 /* are we still OK to delete? */
1509 if ( noErr == theGlobals->result )
1510 {
1511 /* is item locked? */
1512 if ( 0 != (nodeFlags & kFSNodeLockedMask) )
1513 {
1514 /* then attempt to unlock it (ignore result since FSDeleteObject will set it correctly) */
1515 theGlobals->catalogInfo.nodeFlags = nodeFlags & ~kFSNodeLockedMask;
1516 (void) FSSetCatalogInfo(&itemToDelete, kFSCatInfoNodeFlags, &theGlobals->catalogInfo);
1517 }
1518 /* delete the item */
1519 theGlobals->result = FSDeleteObject(&itemToDelete);
1520 }
1521 }
1522 } while ( noErr == theGlobals->result );
1523
1524 /* we found the end of the items normally, so return noErr */
1525 if ( errFSNoMoreItems == theGlobals->result )
1526 {
1527 theGlobals->result = noErr;
1528 }
1529
1530 /* close the FSIterator (closing an open iterator should never fail) */
1531 verify_noerr(FSCloseIterator(iterator));
1532
1533FSOpenIterator:
1534
1535 return;
1536}
1537
1538/*****************************************************************************/
1539
1540OSErr
1541FSDeleteContainerContents(
1542 const FSRef *container)
1543{
1544 FSDeleteContainerGlobals theGlobals;
1545
1546 /* delete container's contents */
1547 FSDeleteContainerLevel(container, &theGlobals);
1548
1549 return ( theGlobals.result );
1550}
1551
1552/*****************************************************************************/
1553
1554OSErr
1555FSDeleteContainer(
1556 const FSRef *container)
1557{
1558 OSErr result;
1559 FSCatalogInfo catalogInfo;
1560
1561 /* get nodeFlags for container */
1562 result = FSGetCatalogInfo(container, kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL,NULL);
1563 require_noerr(result, FSGetCatalogInfo);
1564
1565 /* make sure container is a directory */
1566 require_action(0 != (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), ContainerNotDirectory, result = dirNFErr);
1567
1568 /* delete container's contents */
1569 result = FSDeleteContainerContents(container);
1570 require_noerr(result, FSDeleteContainerContents);
1571
1572 /* is container locked? */
1573 if ( 0 != (catalogInfo.nodeFlags & kFSNodeLockedMask) )
1574 {
1575 /* then attempt to unlock container (ignore result since FSDeleteObject will set it correctly) */
1576 catalogInfo.nodeFlags &= ~kFSNodeLockedMask;
1577 (void) FSSetCatalogInfo(container, kFSCatInfoNodeFlags, &catalogInfo);
1578 }
1579
1580 /* delete the container */
1581 result = FSDeleteObject(container);
1582
1583FSDeleteContainerContents:
1584ContainerNotDirectory:
1585FSGetCatalogInfo:
1586
1587 return ( result );
1588}
1589
1590/*****************************************************************************/
1591
1592/*
1593 The FSIterateContainerLevel function iterates the contents of a container
1594 directory and calls a IterateContainerFilterProc function once for each
1595 file and directory found.
1596
1597 theGlobals --> A pointer to a FSIterateContainerGlobals struct
1598 which contains the variables needed globally by
1599 all recusion levels of FSIterateContainerLevel.
1600 That makes FSIterateContainer thread safe since
1601 each call to it uses its own global world.
1602 It also contains the variables that do not need
1603 to be allocated each time FSIterateContainerLevel
1604 recurses. That lets FSIterateContainerLevel use
1605 less stack space per recursion level.
1606*/
1607
1608static
1609void
1610FSIterateContainerLevel(
1611 FSIterateContainerGlobals *theGlobals)
1612{
1613 FSIterator iterator;
1614
1615 /* If maxLevels is zero, we aren't checking levels */
1616 /* If currentLevel < maxLevels, look at this level */
1617 if ( (theGlobals->maxLevels == 0) ||
1618 (theGlobals->currentLevel < theGlobals->maxLevels) )
1619 {
1620 /* Open FSIterator for flat access to theGlobals->ref */
1621 theGlobals->result = FSOpenIterator(&theGlobals->ref, kFSIterateFlat, &iterator);
1622 require_noerr(theGlobals->result, FSOpenIterator);
1623
1624 ++theGlobals->currentLevel; /* Go to next level */
1625
1626 /* Call FSGetCatalogInfoBulk in loop to get all items in the container */
1627 do
1628 {
1629 theGlobals->result = FSGetCatalogInfoBulk(iterator, 1, &theGlobals->actualObjects,
1630 &theGlobals->containerChanged, theGlobals->whichInfo, &theGlobals->catalogInfo,
1631 &theGlobals->ref, theGlobals->specPtr, theGlobals->namePtr);
1632 if ( (noErr == theGlobals->result || errFSNoMoreItems == theGlobals->result) &&
1633 (0 != theGlobals->actualObjects) )
1634 {
1635 /* Call the IterateFilterProc */
1636 theGlobals->quitFlag = CallIterateContainerFilterProc(theGlobals->iterateFilter,
1637 theGlobals->containerChanged, theGlobals->currentLevel,
1638 &theGlobals->catalogInfo, &theGlobals->ref,
1639 theGlobals->specPtr, theGlobals->namePtr, theGlobals->yourDataPtr);
1640 /* Is it a directory? */
1641 if ( 0 != (theGlobals->catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) )
1642 {
1643 /* Keep going? */
1644 if ( !theGlobals->quitFlag )
1645 {
1646 /* Dive again if the IterateFilterProc didn't say "quit" */
1647 FSIterateContainerLevel(theGlobals);
1648 }
1649 }
1650 }
1651 /* time to fall back a level? */
1652 } while ( (noErr == theGlobals->result) && (!theGlobals->quitFlag) );
1653
1654 /* errFSNoMoreItems is OK - it only means we hit the end of this level */
1655 /* afpAccessDenied is OK, too - it only means we cannot see inside a directory */
1656 if ( (errFSNoMoreItems == theGlobals->result) ||
1657 (afpAccessDenied == theGlobals->result) )
1658 {
1659 theGlobals->result = noErr;
1660 }
1661
1662 --theGlobals->currentLevel; /* Return to previous level as we leave */
1663
1664 /* Close the FSIterator (closing an open iterator should never fail) */
1665 verify_noerr(FSCloseIterator(iterator));
1666 }
1667
1668FSOpenIterator:
1669
1670 return;
1671}
1672
1673/*****************************************************************************/
1674
1675OSErr
1676FSIterateContainer(
1677 const FSRef *container,
1678 ItemCount maxLevels,
1679 FSCatalogInfoBitmap whichInfo,
1680 Boolean wantFSSpec,
1681 Boolean wantName,
1682 IterateContainerFilterProcPtr iterateFilter,
1683 void *yourDataPtr)
1684{
1685 OSErr result;
1686 FSIterateContainerGlobals theGlobals;
1687
1688 /* make sure there is an iterateFilter */
1689 require_action(iterateFilter != NULL, NoIterateFilter, result = paramErr);
1690
1691 /*
1692 * set up the globals we need to access from the recursive routine
1693 */
1694 theGlobals.iterateFilter = iterateFilter;
1695 /* we need the node flags no matter what was requested so we can detect files vs. directories */
1696 theGlobals.whichInfo = whichInfo | kFSCatInfoNodeFlags;
1697 /* start with input container -- the first OpenIterator will ensure it is a directory */
1698 theGlobals.ref = *container;
1699 if ( wantFSSpec )
1700 {
1701 theGlobals.specPtr = &theGlobals.spec;
1702 }
1703 else
1704 {
1705 theGlobals.specPtr = NULL;
1706 }
1707 if ( wantName )
1708 {
1709 theGlobals.namePtr = &theGlobals.name;
1710 }
1711 else
1712 {
1713 theGlobals.namePtr = NULL;
1714 }
1715 theGlobals.yourDataPtr = yourDataPtr;
1716 theGlobals.maxLevels = maxLevels;
1717 theGlobals.currentLevel = 0;
1718 theGlobals.quitFlag = false;
1719 theGlobals.containerChanged = false;
1720 theGlobals.result = noErr;
1721 theGlobals.actualObjects = 0;
1722
1723 /* here we go into recursion land... */
1724 FSIterateContainerLevel(&theGlobals);
1725 result = theGlobals.result;
1726 require_noerr(result, FSIterateContainerLevel);
1727
1728FSIterateContainerLevel:
1729NoIterateFilter:
1730
1731 return ( result );
1732}
1733
1734/*****************************************************************************/
1735
1736OSErr
1737FSGetDirectoryItems(
1738 const FSRef *container,
1739 FSRef ***refsHandle, /* pointer to handle of FSRefs */
1740 ItemCount *numRefs,
1741 Boolean *containerChanged)
1742{
1743 /* Grab items 10 at a time. */
1744 enum { kMaxItemsPerBulkCall = 10 };
1745
1746 OSErr result;
1747 OSErr memResult;
1748 FSIterator iterator;
1749 FSRef refs[kMaxItemsPerBulkCall];
1750 ItemCount actualObjects;
1751 Boolean changed;
1752
1753 /* check parameters */
1754 require_action((NULL != refsHandle) && (NULL != numRefs) && (NULL != containerChanged),
1755 BadParameter, result = paramErr);
1756
1757 *numRefs = 0;
1758 *containerChanged = false;
1759 *refsHandle = (FSRef **)NewHandle(0);
1760 require_action(NULL != *refsHandle, NewHandle, result = memFullErr);
1761
1762 /* open an FSIterator */
1763 result = FSOpenIterator(container, kFSIterateFlat, &iterator);
1764 require_noerr(result, FSOpenIterator);
1765
1766 /* Call FSGetCatalogInfoBulk in loop to get all items in the container */
1767 do
1768 {
1769 result = FSGetCatalogInfoBulk(iterator, kMaxItemsPerBulkCall, &actualObjects,
1770 &changed, kFSCatInfoNone, NULL, refs, NULL, NULL);
1771
1772 /* if the container changed, set containerChanged for output, but keep going */
1773 if ( changed )
1774 {
1775 *containerChanged = changed;
1776 }
1777
1778 /* any result other than noErr and errFSNoMoreItems is serious */
1779 require((noErr == result) || (errFSNoMoreItems == result), FSGetCatalogInfoBulk);
1780
1781 /* add objects to output array and count */
1782 if ( 0 != actualObjects )
1783 {
1784 /* concatenate the FSRefs to the end of the handle */
1785 PtrAndHand(refs, (Handle)*refsHandle, actualObjects * sizeof(FSRef));
1786 memResult = MemError();
1787 require_noerr_action(memResult, MemoryAllocationFailed, result = memResult);
1788
1789 *numRefs += actualObjects;
1790 }
1791 } while ( noErr == result );
1792
1793 verify_noerr(FSCloseIterator(iterator)); /* closing an open iterator should never fail, but... */
1794
1795 return ( noErr );
1796
1797 /**********************/
1798
1799MemoryAllocationFailed:
1800FSGetCatalogInfoBulk:
1801
1802 /* close the iterator */
1803 verify_noerr(FSCloseIterator(iterator));
1804
1805FSOpenIterator:
1806 /* dispose of handle if already allocated and clear the outputs */
1807 if ( NULL != *refsHandle )
1808 {
1809 DisposeHandle((Handle)*refsHandle);
1810 *refsHandle = NULL;
1811 }
1812 *numRefs = 0;
1813
1814NewHandle:
1815BadParameter:
1816
1817 return ( result );
1818}
1819
1820/*****************************************************************************/
1821
1822/*
1823 The GenerateUniqueName function generates a HFSUniStr255 name that is
1824 unique in both dir1 and dir2.
1825
1826 startSeed --> A pointer to a long which is used to generate the
1827 unique name.
1828 <-- It is modified on output to a value which should
1829 be used to generate the next unique name.
1830 dir1 --> The first directory.
1831 dir2 --> The second directory.
1832 uniqueName <-- A pointer to a HFSUniStr255 where the unique name
1833 is to be returned.
1834*/
1835
1836static
1837OSErr
1838GenerateUniqueHFSUniStr(
1839 long *startSeed,
1840 const FSRef *dir1,
1841 const FSRef *dir2,
1842 HFSUniStr255 *uniqueName)
1843{
1844 OSErr result;
1845 long i;
1846 FSRefParam pb;
1847 FSRef newRef;
1848 unsigned char hexStr[17] = "0123456789ABCDEF";
1849
1850 /* set up the parameter block */
1851 pb.name = uniqueName->unicode;
1852 pb.nameLength = 8; /* always 8 characters */
1853 pb.textEncodingHint = kTextEncodingUnknown;
1854 pb.newRef = &newRef;
1855
1856 /* loop until we get fnfErr with a filename in both directories */
1857 result = noErr;
1858 while ( fnfErr != result )
1859 {
1860 /* convert startSeed to 8 character Unicode string */
1861 uniqueName->length = 8;
1862 for ( i = 0; i < 8; ++i )
1863 {
1864 uniqueName->unicode[i] = hexStr[((*startSeed >> ((7-i)*4)) & 0xf)];
1865 }
1866
1867 /* try in dir1 */
1868 pb.ref = dir1;
1869 result = PBMakeFSRefUnicodeSync(&pb);
1870 if ( fnfErr == result )
1871 {
1872 /* try in dir2 */
1873 pb.ref = dir2;
1874 result = PBMakeFSRefUnicodeSync(&pb);
1875 if ( fnfErr != result )
1876 {
1877 /* exit if anything other than noErr or fnfErr */
1878 require_noerr(result, Dir2PBMakeFSRefUnicodeSyncFailed);
1879 }
1880 }
1881 else
1882 {
1883 /* exit if anything other than noErr or fnfErr */
1884 require_noerr(result, Dir1PBMakeFSRefUnicodeSyncFailed);
1885 }
1886
1887 /* increment seed for next pass through loop, */
1888 /* or for next call to GenerateUniqueHFSUniStr */
1889 ++(*startSeed);
1890 }
1891
1892 /* we have a unique file name which doesn't exist in dir1 or dir2 */
1893 result = noErr;
1894
1895Dir2PBMakeFSRefUnicodeSyncFailed:
1896Dir1PBMakeFSRefUnicodeSyncFailed:
1897
1898 return ( result );
1899}
1900
1901/*****************************************************************************/
1902
1903OSErr
1904FSExchangeObjectsCompat(
1905 const FSRef *sourceRef,
1906 const FSRef *destRef,
1907 FSRef *newSourceRef,
1908 FSRef *newDestRef)
1909{
1910 enum
1911 {
1912 /* get all settable info except for mod dates, plus the volume refNum and parent directory ID */
1913 kGetCatInformationMask = (kFSCatInfoSettableInfo |
1914 kFSCatInfoVolume |
1915 kFSCatInfoParentDirID) &
1916 ~(kFSCatInfoContentMod | kFSCatInfoAttrMod),
1917 /* set everything possible except for mod dates */
1918 kSetCatinformationMask = kFSCatInfoSettableInfo &
1919 ~(kFSCatInfoContentMod | kFSCatInfoAttrMod)
1920 };
1921
1922 OSErr result;
1923 GetVolParmsInfoBuffer volParmsInfo;
1924 UInt32 infoSize;
1925 FSCatalogInfo sourceCatalogInfo; /* source file's catalog information */
1926 FSCatalogInfo destCatalogInfo; /* destination file's catalog information */
1927 HFSUniStr255 sourceName; /* source file's Unicode name */
1928 HFSUniStr255 destName; /* destination file's Unicode name */
1929 FSRef sourceCurrentRef; /* FSRef to current location of source file throughout this function */
1930 FSRef destCurrentRef; /* FSRef to current location of destination file throughout this function */
1931 FSRef sourceParentRef; /* FSRef to parent directory of source file */
1932 FSRef destParentRef; /* FSRef to parent directory of destination file */
1933 HFSUniStr255 sourceUniqueName; /* unique name given to source file while exchanging it with destination */
1934 HFSUniStr255 destUniqueName; /* unique name given to destination file while exchanging it with source */
1935 long theSeed; /* the seed for generating unique names */
1936 Boolean sameParentDirs; /* true if source and destinatin parent directory is the same */
1937
1938 /* check parameters */
1939 require_action((NULL != newSourceRef) && (NULL != newDestRef), BadParameter, result = paramErr);
1940
1941 /* output refs and current refs = input refs to start with */
1942 BlockMoveData(sourceRef, newSourceRef, sizeof(FSRef));
1943 BlockMoveData(sourceRef, &sourceCurrentRef, sizeof(FSRef));
1944
1945 BlockMoveData(destRef, newDestRef, sizeof(FSRef));
1946 BlockMoveData(destRef, &destCurrentRef, sizeof(FSRef));
1947
1948 /* get source volume's vRefNum */
1949 result = FSGetCatalogInfo(&sourceCurrentRef, kFSCatInfoVolume, &sourceCatalogInfo, NULL, NULL, NULL);
1950 require_noerr(result, DetermineSourceVRefNumFailed);
1951
1952 /* see if that volume supports FSExchangeObjects */
1953 result = FSGetVolParms(sourceCatalogInfo.volume, sizeof(GetVolParmsInfoBuffer),
1954 &volParmsInfo, &infoSize);
1955 if ( (noErr == result) && VolSupportsFSExchangeObjects(&volParmsInfo) )
1956 {
1957 /* yes - use FSExchangeObjects */
1958 result = FSExchangeObjects(sourceRef, destRef);
1959 }
1960 else
1961 {
1962 /* no - emulate FSExchangeObjects */
1963
1964 /* Note: The compatibility case won't work for files with *Btree control blocks. */
1965 /* Right now the only *Btree files are created by the system. */
1966
1967 /* get all catalog information and Unicode names for each file */
1968 result = FSGetCatalogInfo(&sourceCurrentRef, kGetCatInformationMask, &sourceCatalogInfo, &sourceName, NULL, &sourceParentRef);
1969 require_noerr(result, SourceFSGetCatalogInfoFailed);
1970
1971 result = FSGetCatalogInfo(&destCurrentRef, kGetCatInformationMask, &destCatalogInfo, &destName, NULL, &destParentRef);
1972 require_noerr(result, DestFSGetCatalogInfoFailed);
1973
1974 /* make sure source and destination are on same volume */
1975 require_action(sourceCatalogInfo.volume == destCatalogInfo.volume, NotSameVolume, result = diffVolErr);
1976
1977 /* make sure both files are *really* files */
1978 require_action((0 == (sourceCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) &&
1979 (0 == (destCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)), NotAFile, result = notAFileErr);
1980
1981 /* generate 2 names that are unique in both directories */
1982 theSeed = 0x4a696d4c; /* a fine unlikely filename */
1983
1984 result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &sourceUniqueName);
1985 require_noerr(result, GenerateUniqueHFSUniStr1Failed);
1986
1987 result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &destUniqueName);
1988 require_noerr(result, GenerateUniqueHFSUniStr2Failed);
1989
1990 /* rename sourceCurrentRef to sourceUniqueName */
1991 result = FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef);
1992 require_noerr(result, FSRenameUnicode1Failed);
1993 BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef));
1994
1995 /* rename destCurrentRef to destUniqueName */
1996 result = FSRenameUnicode(&destCurrentRef, destUniqueName.length, destUniqueName.unicode, kTextEncodingUnknown, newDestRef);
1997 require_noerr(result, FSRenameUnicode2Failed);
1998 BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef));
1999
2000 /* are the source and destination parent directories the same? */
2001 sameParentDirs = ( sourceCatalogInfo.parentDirID == destCatalogInfo.parentDirID );
2002 if ( !sameParentDirs )
2003 {
2004 /* move source file to dest parent directory */
2005 result = FSMoveObject(&sourceCurrentRef, &destParentRef, newSourceRef);
2006 require_noerr(result, FSMoveObject1Failed);
2007 BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef));
2008
2009 /* move dest file to source parent directory */
2010 result = FSMoveObject(&destCurrentRef, &sourceParentRef, newDestRef);
2011 require_noerr(result, FSMoveObject2Failed);
2012 BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef));
2013 }
2014
2015 /* At this point, the files are in their new locations (if they were moved). */
2016 /* The source file is named sourceUniqueName and is in the directory referred to */
2017 /* by destParentRef. The destination file is named destUniqueName and is in the */
2018 /* directory referred to by sourceParentRef. */
2019
2020 /* give source file the dest file's catalog information except for mod dates */
2021 result = FSSetCatalogInfo(&sourceCurrentRef, kSetCatinformationMask, &destCatalogInfo);
2022 require_noerr(result, FSSetCatalogInfo1Failed);
2023
2024 /* give dest file the source file's catalog information except for mod dates */
2025 result = FSSetCatalogInfo(&destCurrentRef, kSetCatinformationMask, &sourceCatalogInfo);
2026 require_noerr(result, FSSetCatalogInfo2Failed);
2027
2028 /* rename source file with dest file's name */
2029 result = FSRenameUnicode(&sourceCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newSourceRef);
2030 require_noerr(result, FSRenameUnicode3Failed);
2031 BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef));
2032
2033 /* rename dest file with source file's name */
2034 result = FSRenameUnicode(&destCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newDestRef);
2035 require_noerr(result, FSRenameUnicode4Failed);
2036
2037 /* we're done with no errors, so swap newSourceRef and newDestRef */
2038 BlockMoveData(newDestRef, newSourceRef, sizeof(FSRef));
2039 BlockMoveData(&sourceCurrentRef, newDestRef, sizeof(FSRef));
2040 }
2041
2042 return ( result );
2043
2044 /**********************/
2045
2046/* If there are any failures while emulating FSExchangeObjects, attempt to reverse any steps */
2047/* already taken. In any case, newSourceRef and newDestRef will refer to the files in whatever */
2048/* state and location they ended up in so that both files can be found by the calling code. */
2049
2050FSRenameUnicode4Failed:
2051
2052 /* attempt to rename source file to sourceUniqueName */
2053 if ( noErr == FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef) )
2054 {
2055 BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef));
2056 }
2057
2058FSRenameUnicode3Failed:
2059
2060 /* attempt to restore dest file's catalog information */
2061 verify_noerr(FSSetCatalogInfo(&destCurrentRef, kFSCatInfoSettableInfo, &destCatalogInfo));
2062
2063FSSetCatalogInfo2Failed:
2064
2065 /* attempt to restore source file's catalog information */
2066 verify_noerr(FSSetCatalogInfo(&sourceCurrentRef, kFSCatInfoSettableInfo, &sourceCatalogInfo));
2067
2068FSSetCatalogInfo1Failed:
2069
2070 if ( !sameParentDirs )
2071 {
2072 /* attempt to move dest file back to dest directory */
2073 if ( noErr == FSMoveObject(&destCurrentRef, &destParentRef, newDestRef) )
2074 {
2075 BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef));
2076 }
2077 }
2078
2079FSMoveObject2Failed:
2080
2081 if ( !sameParentDirs )
2082 {
2083 /* attempt to move source file back to source directory */
2084 if ( noErr == FSMoveObject(&sourceCurrentRef, &sourceParentRef, newSourceRef) )
2085 {
2086 BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef));
2087 }
2088 }
2089
2090FSMoveObject1Failed:
2091
2092 /* attempt to rename dest file to original name */
2093 verify_noerr(FSRenameUnicode(&destCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newDestRef));
2094
2095FSRenameUnicode2Failed:
2096
2097 /* attempt to rename source file to original name */
2098 verify_noerr(FSRenameUnicode(&sourceCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newSourceRef));
2099
2100FSRenameUnicode1Failed:
2101GenerateUniqueHFSUniStr2Failed:
2102GenerateUniqueHFSUniStr1Failed:
2103NotAFile:
2104NotSameVolume:
2105DestFSGetCatalogInfoFailed:
2106SourceFSGetCatalogInfoFailed:
2107DetermineSourceVRefNumFailed:
2108BadParameter:
2109
2110 return ( result );
2111}
2112
2113/*****************************************************************************/
2114
2115#pragma mark ----- Shared Environment Routines -----
2116
2117/*****************************************************************************/
2118
2119OSErr
2120MoreFiles_FSLockRange(
2121 SInt16 refNum,
2122 SInt32 rangeLength,
2123 SInt32 rangeStart)
2124{
2125 OSErr result;
2126 ParamBlockRec pb;
2127
2128 pb.ioParam.ioRefNum = refNum;
2129 pb.ioParam.ioReqCount = rangeLength;
2130 pb.ioParam.ioPosMode = fsFromStart;
2131 pb.ioParam.ioPosOffset = rangeStart;
2132 result = PBLockRangeSync(&pb);
2133 require_noerr(result, PBLockRangeSync);
2134
2135PBLockRangeSync:
2136
2137 return ( result );
2138}
2139
2140/*****************************************************************************/
2141
2142OSErr
2143MoreFiles_FSUnlockRange(
2144 SInt16 refNum,
2145 SInt32 rangeLength,
2146 SInt32 rangeStart)
2147{
2148 OSErr result;
2149 ParamBlockRec pb;
2150
2151 pb.ioParam.ioRefNum = refNum;
2152 pb.ioParam.ioReqCount = rangeLength;
2153 pb.ioParam.ioPosMode = fsFromStart;
2154 pb.ioParam.ioPosOffset = rangeStart;
2155 result = PBUnlockRangeSync(&pb);
2156 require_noerr(result, PBUnlockRangeSync);
2157
2158PBUnlockRangeSync:
2159
2160 return ( result );
2161}
2162
2163/*****************************************************************************/
2164
2165OSErr
2166FSGetDirAccess(
2167 const FSRef *ref,
2168 SInt32 *ownerID, /* can be NULL */
2169 SInt32 *groupID, /* can be NULL */
2170 SInt32 *accessRights) /* can be NULL */
2171{
2172 OSErr result;
2173 FSSpec spec;
2174 HParamBlockRec pb;
2175
2176 /* get FSSpec from FSRef */
2177 result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL);
2178 require_noerr(result, FSGetCatalogInfo);
2179
2180 /* get directory access info for FSSpec */
2181 pb.accessParam.ioNamePtr = (StringPtr)spec.name;
2182 pb.accessParam.ioVRefNum = spec.vRefNum;
2183 pb.fileParam.ioDirID = spec.parID;
2184 result = PBHGetDirAccessSync(&pb);
2185 require_noerr(result, PBHGetDirAccessSync);
2186
2187 /* return the IDs and access rights */
2188 if ( NULL != ownerID )
2189 {
2190 *ownerID = pb.accessParam.ioACOwnerID;
2191 }
2192 if ( NULL != groupID )
2193 {
2194 *groupID = pb.accessParam.ioACGroupID;
2195 }
2196 if ( NULL != accessRights )
2197 {
2198 *accessRights = pb.accessParam.ioACAccess;
2199 }
2200
2201PBHGetDirAccessSync:
2202FSGetCatalogInfo:
2203
2204 return ( result );
2205}
2206
2207/*****************************************************************************/
2208
2209OSErr
2210FSSetDirAccess(
2211 const FSRef *ref,
2212 SInt32 ownerID,
2213 SInt32 groupID,
2214 SInt32 accessRights)
2215{
2216 OSErr result;
2217 FSSpec spec;
2218 HParamBlockRec pb;
2219
2220 enum
2221 {
2222 /* Just the bits that can be set */
2223 kSetDirAccessSettableMask = (kioACAccessBlankAccessMask +
2224 kioACAccessEveryoneWriteMask + kioACAccessEveryoneReadMask + kioACAccessEveryoneSearchMask +
2225 kioACAccessGroupWriteMask + kioACAccessGroupReadMask + kioACAccessGroupSearchMask +
2226 kioACAccessOwnerWriteMask + kioACAccessOwnerReadMask + kioACAccessOwnerSearchMask)
2227 };
2228
2229 /* get FSSpec from FSRef */
2230 result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL);
2231 require_noerr(result, FSGetCatalogInfo);
2232
2233 /* set directory access info for FSSpec */
2234 pb.accessParam.ioNamePtr = (StringPtr)spec.name;
2235 pb.accessParam.ioVRefNum = spec.vRefNum;
2236 pb.fileParam.ioDirID = spec.parID;
2237 pb.accessParam.ioACOwnerID = ownerID;
2238 pb.accessParam.ioACGroupID = groupID;
2239 pb.accessParam.ioACAccess = accessRights & kSetDirAccessSettableMask;
2240 result = PBHSetDirAccessSync(&pb);
2241 require_noerr(result, PBHSetDirAccessSync);
2242
2243PBHSetDirAccessSync:
2244FSGetCatalogInfo:
2245
2246 return ( result );
2247}
2248
2249/*****************************************************************************/
2250
2251OSErr
2252FSGetVolMountInfoSize(
2253 FSVolumeRefNum volRefNum,
2254 SInt16 *size)
2255{
2256 OSErr result;
2257 ParamBlockRec pb;
2258
2259 /* check parameters */
2260 require_action(NULL != size, BadParameter, result = paramErr);
2261
2262 pb.ioParam.ioNamePtr = NULL;
2263 pb.ioParam.ioVRefNum = volRefNum;
2264 pb.ioParam.ioBuffer = (Ptr)size;
2265 result = PBGetVolMountInfoSize(&pb);
2266 require_noerr(result, PBGetVolMountInfoSize);
2267
2268PBGetVolMountInfoSize:
2269BadParameter:
2270
2271 return ( result );
2272}
2273
2274/*****************************************************************************/
2275
2276OSErr
2277FSGetVolMountInfo(
2278 FSVolumeRefNum volRefNum,
2279 void *volMountInfo)
2280{
2281 OSErr result;
2282 ParamBlockRec pb;
2283
2284 /* check parameters */
2285 require_action(NULL != volMountInfo, BadParameter, result = paramErr);
2286
2287 pb.ioParam.ioNamePtr = NULL;
2288 pb.ioParam.ioVRefNum = volRefNum;
2289 pb.ioParam.ioBuffer = (Ptr)volMountInfo;
2290 result = PBGetVolMountInfo(&pb);
2291 require_noerr(result, PBGetVolMountInfo);
2292
2293PBGetVolMountInfo:
2294BadParameter:
2295
2296 return ( result );
2297}
2298
2299/*****************************************************************************/
2300
2301OSErr
2302FSVolumeMount(
2303 const void *volMountInfo,
2304 FSVolumeRefNum *volRefNum)
2305{
2306 OSErr result;
2307 ParamBlockRec pb;
2308
2309 /* check parameters */
2310 require_action(NULL != volRefNum, BadParameter, result = paramErr);
2311
2312 pb.ioParam.ioBuffer = (Ptr)volMountInfo;
2313 result = PBVolumeMount(&pb);
2314 require_noerr(result, PBVolumeMount);
2315
2316 /* return the volume reference number */
2317 *volRefNum = pb.ioParam.ioVRefNum;
2318
2319PBVolumeMount:
2320BadParameter:
2321
2322 return ( result );
2323}
2324
2325/*****************************************************************************/
2326
2327OSErr
2328FSMapID(
2329 FSVolumeRefNum volRefNum,
2330 SInt32 ugID,
2331 SInt16 objType,
2332 Str31 name)
2333{
2334 OSErr result;
2335 HParamBlockRec pb;
2336
2337 /* check parameters */
2338 require_action(NULL != name, BadParameter, result = paramErr);
2339
2340 pb.objParam.ioNamePtr = NULL;
2341 pb.objParam.ioVRefNum = volRefNum;
2342 pb.objParam.ioObjType = objType;
2343 pb.objParam.ioObjNamePtr = name;
2344 pb.objParam.ioObjID = ugID;
2345 result = PBHMapIDSync(&pb);
2346 require_noerr(result, PBHMapIDSync);
2347
2348PBHMapIDSync:
2349BadParameter:
2350
2351 return ( result );
2352}
2353
2354/*****************************************************************************/
2355
2356OSErr
2357FSMapName(
2358 FSVolumeRefNum volRefNum,
2359 ConstStr255Param name,
2360 SInt16 objType,
2361 SInt32 *ugID)
2362{
2363 OSErr result;
2364 HParamBlockRec pb;
2365
2366 /* check parameters */
2367 require_action(NULL != ugID, BadParameter, result = paramErr);
2368
2369 pb.objParam.ioNamePtr = NULL;
2370 pb.objParam.ioVRefNum = volRefNum;
2371 pb.objParam.ioObjType = objType;
2372 pb.objParam.ioObjNamePtr = (StringPtr)name;
2373 result = PBHMapNameSync(&pb);
2374 require_noerr(result, PBHMapNameSync);
2375
2376 /* return the user or group ID */
2377 *ugID = pb.objParam.ioObjID;
2378
2379PBHMapNameSync:
2380BadParameter:
2381
2382 return ( result );
2383}
2384
2385/*****************************************************************************/
2386
2387OSErr
2388FSCopyFile(
2389 const FSRef *srcFileRef,
2390 const FSRef *dstDirectoryRef,
2391 UniCharCount nameLength,
2392 const UniChar *copyName, /* can be NULL (no rename during copy) */
2393 TextEncoding textEncodingHint,
2394 FSRef *newRef) /* can be NULL */
2395{
2396 OSErr result;
2397 FSSpec srcFileSpec;
2398 FSCatalogInfo catalogInfo;
2399 HParamBlockRec pb;
2400 Str31 hfsName;
2401 GetVolParmsInfoBuffer volParmsInfo;
2402 UInt32 infoSize;
2403
2404 /* get source FSSpec from source FSRef */
2405 result = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL);
2406 require_noerr(result, FSGetCatalogInfo_srcFileRef);
2407
2408 /* Make sure the volume supports CopyFile */
2409 result = FSGetVolParms(srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer),
2410 &volParmsInfo, &infoSize);
2411 require_action((noErr == result) && VolHasCopyFile(&volParmsInfo),
2412 NoCopyFileSupport, result = paramErr);
2413
2414 /* get destination volume reference number and destination directory ID from destination FSRef */
2415 result = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume + kFSCatInfoNodeID,
2416 &catalogInfo, NULL, NULL, NULL);
2417 require_noerr(result, FSGetCatalogInfo_dstDirectoryRef);
2418
2419 /* tell the server to copy the object */
2420 pb.copyParam.ioVRefNum = srcFileSpec.vRefNum;
2421 pb.copyParam.ioDirID = srcFileSpec.parID;
2422 pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name;
2423 pb.copyParam.ioDstVRefNum = catalogInfo.volume;
2424 pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID;
2425 pb.copyParam.ioNewName = NULL;
2426 if ( NULL != copyName )
2427 {
2428 result = UnicodeNameGetHFSName(nameLength, copyName, textEncodingHint, false, hfsName);
2429 require_noerr(result, UnicodeNameGetHFSName);
2430
2431 pb.copyParam.ioCopyName = hfsName;
2432 }
2433 else
2434 {
2435 pb.copyParam.ioCopyName = NULL;
2436 }
2437 result = PBHCopyFileSync(&pb);
2438 require_noerr(result, PBHCopyFileSync);
2439
2440 if ( NULL != newRef )
2441 {
2442 verify_noerr(FSMakeFSRef(pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID,
2443 pb.copyParam.ioCopyName, newRef));
2444 }
2445
2446PBHCopyFileSync:
2447UnicodeNameGetHFSName:
2448FSGetCatalogInfo_dstDirectoryRef:
2449NoCopyFileSupport:
2450FSGetCatalogInfo_srcFileRef:
2451
2452 return ( result );
2453}
2454
2455/*****************************************************************************/
2456
2457OSErr
2458FSMoveRename(
2459 const FSRef *srcFileRef,
2460 const FSRef *dstDirectoryRef,
2461 UniCharCount nameLength,
2462 const UniChar *moveName, /* can be NULL (no rename during move) */
2463 TextEncoding textEncodingHint,
2464 FSRef *newRef) /* can be NULL */
2465{
2466 OSErr result;
2467 FSSpec srcFileSpec;
2468 FSCatalogInfo catalogInfo;
2469 HParamBlockRec pb;
2470 Str31 hfsName;
2471 GetVolParmsInfoBuffer volParmsInfo;
2472 UInt32 infoSize;
2473
2474 /* get source FSSpec from source FSRef */
2475 result = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL);
2476 require_noerr(result, FSGetCatalogInfo_srcFileRef);
2477
2478 /* Make sure the volume supports MoveRename */
2479 result = FSGetVolParms(srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer),
2480 &volParmsInfo, &infoSize);
2481 require_action((noErr == result) && VolHasMoveRename(&volParmsInfo),
2482 NoMoveRenameSupport, result = paramErr);
2483
2484 /* get destination volume reference number and destination directory ID from destination FSRef */
2485 result = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume + kFSCatInfoNodeID,
2486 &catalogInfo, NULL, NULL, NULL);
2487 require_noerr(result, FSGetCatalogInfo_dstDirectoryRef);
2488
2489 /* make sure the source and destination are on the same volume */
2490 require_action(srcFileSpec.vRefNum == catalogInfo.volume, NotSameVolume, result = diffVolErr);
2491
2492 /* tell the server to move and rename the object */
2493 pb.copyParam.ioVRefNum = srcFileSpec.vRefNum;
2494 pb.copyParam.ioDirID = srcFileSpec.parID;
2495 pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name;
2496 pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID;
2497 pb.copyParam.ioNewName = NULL;
2498 if ( NULL != moveName )
2499 {
2500 result = UnicodeNameGetHFSName(nameLength, moveName, textEncodingHint, false, hfsName);
2501 require_noerr(result, UnicodeNameGetHFSName);
2502
2503 pb.copyParam.ioCopyName = hfsName;
2504 }
2505 else
2506 {
2507 pb.copyParam.ioCopyName = NULL;
2508 }
2509 result = PBHMoveRenameSync(&pb);
2510 require_noerr(result, PBHMoveRenameSync);
2511
2512 if ( NULL != newRef )
2513 {
2514 verify_noerr(FSMakeFSRef(pb.copyParam.ioVRefNum, pb.copyParam.ioNewDirID,
2515 pb.copyParam.ioCopyName, newRef));
2516 }
2517
2518PBHMoveRenameSync:
2519UnicodeNameGetHFSName:
2520NotSameVolume:
2521FSGetCatalogInfo_dstDirectoryRef:
2522NoMoveRenameSupport:
2523FSGetCatalogInfo_srcFileRef:
2524
2525 return ( result );
2526}
2527
2528/*****************************************************************************/
2529
2530#pragma mark ----- File ID Routines -----
2531
2532/*****************************************************************************/
2533
2534OSErr
2535FSResolveFileIDRef(
2536 FSVolumeRefNum volRefNum,
2537 SInt32 fileID,
2538 FSRef *ref)
2539{
2540 OSErr result;
2541 FIDParam pb;
2542 Str255 tempStr;
2543
2544 /* check parameters */
2545 require_action(NULL != ref, BadParameter, result = paramErr);
2546
2547 /* resolve the file ID reference */
2548 tempStr[0] = 0;
2549 pb.ioNamePtr = tempStr;
2550 pb.ioVRefNum = volRefNum;
2551 pb.ioFileID = fileID;
2552 result = PBResolveFileIDRefSync((HParmBlkPtr)&pb);
2553 require_noerr(result, PBResolveFileIDRefSync);
2554
2555 /* and then make an FSRef to the file */
2556 result = FSMakeFSRef(volRefNum, pb.ioSrcDirID, tempStr, ref);
2557 require_noerr(result, FSMakeFSRef);
2558
2559FSMakeFSRef:
2560PBResolveFileIDRefSync:
2561BadParameter:
2562
2563 return ( result );
2564}
2565
2566/*****************************************************************************/
2567
2568OSErr
2569FSCreateFileIDRef(
2570 const FSRef *ref,
2571 SInt32 *fileID)
2572{
2573 OSErr result;
2574 FSSpec spec;
2575 FIDParam pb;
2576
2577 /* check parameters */
2578 require_action(NULL != fileID, BadParameter, result = paramErr);
2579
2580 /* Get an FSSpec from the FSRef */
2581 result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL);
2582 require_noerr(result, FSGetCatalogInfo);
2583
2584 /* Create (or get) the file ID reference using the FSSpec */
2585 pb.ioNamePtr = (StringPtr)spec.name;
2586 pb.ioVRefNum = spec.vRefNum;
2587 pb.ioSrcDirID = spec.parID;
2588 result = PBCreateFileIDRefSync((HParmBlkPtr)&pb);
2589 require((noErr == result) || (fidExists == result) || (afpIDExists == result),
2590 PBCreateFileIDRefSync);
2591
2592 /* return the file ID reference */
2593 *fileID = pb.ioFileID;
2594
2595PBCreateFileIDRefSync:
2596FSGetCatalogInfo:
2597BadParameter:
2598
2599 return ( result );
2600}
2601
2602/*****************************************************************************/
2603
2604#pragma mark ----- Utility Routines -----
2605
2606/*****************************************************************************/
2607
2608Ptr
2609GetTempBuffer(
2610 ByteCount buffReqSize,
2611 ByteCount *buffActSize)
2612{
2613 enum
2614 {
2615 kSlopMemory = 0x00008000 /* 32K - Amount of free memory to leave when allocating buffers */
2616 };
2617
2618 Ptr tempPtr;
2619
2620 /* check parameters */
2621 require_action(NULL != buffActSize, BadParameter, tempPtr = NULL);
2622
2623 /* Make request a multiple of 4K bytes */
2624 buffReqSize = buffReqSize & 0xfffff000;
2625
2626 if ( buffReqSize < 0x00001000 )
2627 {
2628 /* Request was smaller than 4K bytes - make it 4K */
2629 buffReqSize = 0x00001000;
2630 }
2631
2632 /* Attempt to allocate the memory */
2633 tempPtr = NewPtr(buffReqSize);
2634
2635 /* If request failed, go to backup plan */
2636 if ( (tempPtr == NULL) && (buffReqSize > 0x00001000) )
2637 {
2638 /*
2639 ** Try to get largest 4K byte block available
2640 ** leaving some slop for the toolbox if possible
2641 */
2642 long freeMemory = (FreeMem() - kSlopMemory) & 0xfffff000;
2643
2644 buffReqSize = MaxBlock() & 0xfffff000;
2645
2646 if ( buffReqSize > freeMemory )
2647 {
2648 buffReqSize = freeMemory;
2649 }
2650
2651 if ( buffReqSize == 0 )
2652 {
2653 buffReqSize = 0x00001000;
2654 }
2655
2656 tempPtr = NewPtr(buffReqSize);
2657 }
2658
2659 /* Return bytes allocated */
2660 if ( tempPtr != NULL )
2661 {
2662 *buffActSize = buffReqSize;
2663 }
2664 else
2665 {
2666 *buffActSize = 0;
2667 }
2668
2669BadParameter:
2670
2671 return ( tempPtr );
2672}
2673
2674/*****************************************************************************/
2675
2676OSErr
2677FileRefNumGetFSRef(
2678 short refNum,
2679 FSRef *ref)
2680{
2681 return ( FSGetForkCBInfo(refNum, 0, NULL, NULL, NULL, ref, NULL) );
2682}
2683
2684/*****************************************************************************/
2685
2686OSErr
2687FSSetDefault(
2688 const FSRef *newDefault,
2689 FSRef *oldDefault)
2690{
2691 OSErr result;
2692 FSVolumeRefNum vRefNum;
2693 long dirID;
2694 FSCatalogInfo catalogInfo;
2695
2696 /* check parameters */
2697 require_action((NULL != newDefault) && (NULL != oldDefault), BadParameter, result = paramErr);
2698
2699 /* Get nodeFlags, vRefNum and dirID (nodeID) of newDefault */
2700 result = FSGetCatalogInfo(newDefault,
2701 kFSCatInfoNodeFlags + kFSCatInfoVolume + kFSCatInfoNodeID,
2702 &catalogInfo, NULL, NULL, NULL);
2703 require_noerr(result, FSGetCatalogInfo);
2704
2705 /* Make sure newDefault is a directory */
2706 require_action(0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags), NewDefaultNotDirectory,
2707 result = dirNFErr);
2708
2709 /* Get the current working directory. */
2710 result = HGetVol(NULL, &vRefNum, &dirID);
2711 require_noerr(result, HGetVol);
2712
2713 /* Return the oldDefault FSRef */
2714 result = FSMakeFSRef(vRefNum, dirID, NULL, oldDefault);
2715 require_noerr(result, FSMakeFSRef);
2716
2717 /* Set the new current working directory */
2718 result = HSetVol(NULL, catalogInfo.volume, catalogInfo.nodeID);
2719 require_noerr(result, HSetVol);
2720
2721HSetVol:
2722FSMakeFSRef:
2723HGetVol:
2724NewDefaultNotDirectory:
2725FSGetCatalogInfo:
2726BadParameter:
2727
2728 return ( result );
2729}
2730
2731/*****************************************************************************/
2732
2733OSErr
2734FSRestoreDefault(
2735 const FSRef *oldDefault)
2736{
2737 OSErr result;
2738 FSCatalogInfo catalogInfo;
2739
2740 /* check parameters */
2741 require_action(NULL != oldDefault, BadParameter, result = paramErr);
2742
2743 /* Get nodeFlags, vRefNum and dirID (nodeID) of oldDefault */
2744 result = FSGetCatalogInfo(oldDefault,
2745 kFSCatInfoNodeFlags + kFSCatInfoVolume + kFSCatInfoNodeID,
2746 &catalogInfo, NULL, NULL, NULL);
2747 require_noerr(result, FSGetCatalogInfo);
2748
2749 /* Make sure oldDefault is a directory */
2750 require_action(0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags), OldDefaultNotDirectory,
2751 result = dirNFErr);
2752
2753 /* Set the current working directory to oldDefault */
2754 result = HSetVol(NULL, catalogInfo.volume, catalogInfo.nodeID);
2755 require_noerr(result, HSetVol);
2756
2757HSetVol:
2758OldDefaultNotDirectory:
2759FSGetCatalogInfo:
2760BadParameter:
2761
2762 return ( result );
2763}
2764
2765/*****************************************************************************/
diff --git a/linden/indra/mac_updater/MoreFilesX.h b/linden/indra/mac_updater/MoreFilesX.h
new file mode 100644
index 0000000..ce3be86
--- /dev/null
+++ b/linden/indra/mac_updater/MoreFilesX.h
@@ -0,0 +1,1848 @@
1/**
2 * @file MoreFilesX.h
3 * @brief Mac OS X updater program
4 *
5 * Copyright (c) 2004-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28/*
29 File: MoreFilesX.h
30
31 Contains: A collection of useful high-level File Manager routines
32 which use the HFS Plus APIs wherever possible.
33
34 Version: MoreFilesX 1.0.1
35
36 Copyright: � 1992-2002 by Apple Computer, Inc., all rights reserved.
37
38 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
39 ("Apple") in consideration of your agreement to the following terms, and your
40 use, installation, modification or redistribution of this Apple software
41 constitutes acceptance of these terms. If you do not agree with these terms,
42 please do not use, install, modify or redistribute this Apple software.
43
44 In consideration of your agreement to abide by the following terms, and subject
45 to these terms, Apple grants you a personal, non-exclusive license, under Apple�s
46 copyrights in this original Apple software (the "Apple Software"), to use,
47 reproduce, modify and redistribute the Apple Software, with or without
48 modifications, in source and/or binary forms; provided that if you redistribute
49 the Apple Software in its entirety and without modifications, you must retain
50 this notice and the following text and disclaimers in all such redistributions of
51 the Apple Software. Neither the name, trademarks, service marks or logos of
52 Apple Computer, Inc. may be used to endorse or promote products derived from the
53 Apple Software without specific prior written permission from Apple. Except as
54 expressly stated in this notice, no other rights or licenses, express or implied,
55 are granted by Apple herein, including but not limited to any patent rights that
56 may be infringed by your derivative works or by other works in which the Apple
57 Software may be incorporated.
58
59 The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
60 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
61 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
62 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
63 COMBINATION WITH YOUR PRODUCTS.
64
65 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
66 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
67 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
68 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
69 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
70 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
71 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
72
73 File Ownership:
74
75 DRI: Apple Macintosh Developer Technical Support
76
77 Other Contact: For bug reports, consult the following page on
78 the World Wide Web:
79 http://developer.apple.com/bugreporter/
80
81 Technology: DTS Sample Code
82
83 Writers:
84
85 (JL) Jim Luther
86
87 Change History (most recent first):
88
89 <3> 4/19/02 JL [2853905] Fixed #if test around header includes.
90 <2> 4/19/02 JL [2853901] Updated standard disclaimer.
91 <1> 1/25/02 JL MoreFilesX 1.0
92
93 Notes:
94 What do those arrows in the documentation for each routine mean?
95
96 --> The parameter is an input
97
98 <-- The parameter is an output. The pointer to the variable
99 where the output will be returned (must not be NULL).
100
101 <** The parameter is an optional output. If it is not a
102 NULL pointer, it points to the variable where the output
103 will be returned. If it is a NULL pointer, the output will
104 not be returned and will possibly let the routine and the
105 File Manager do less work. If you don't need an optional output,
106 don't ask for it.
107 **> The parameter is an optional input. If it is not a
108 NULL pointer, it points to the variable containing the
109 input data. If it is a NULL pointer, the input is not used
110 and will possibly let the routine and the File Manager
111 do less work.
112*/
113
114#ifndef __MOREFILESX__
115#define __MOREFILESX__
116
117#ifndef __CARBON__
118 #include <Carbon/Carbon.h>
119#endif
120
121#if PRAGMA_ONCE
122#pragma once
123#endif
124
125#ifdef __cplusplus
126extern "C" {
127#endif
128
129#if PRAGMA_IMPORT
130#pragma import on
131#endif
132
133#if PRAGMA_STRUCT_ALIGN
134 #pragma options align=mac68k
135#elif PRAGMA_STRUCT_PACKPUSH
136 #pragma pack(push, 2)
137#elif PRAGMA_STRUCT_PACK
138 #pragma pack(2)
139#endif
140
141/*****************************************************************************/
142
143#pragma mark ----- FinderInfo and ExtendedFinderInfo -----
144
145/*
146 * FSGetFinderInfo and FSSetFinderInfo use these unions for Finder information.
147 */
148
149union FinderInfo
150{
151 FileInfo file;
152 FolderInfo folder;
153};
154typedef union FinderInfo FinderInfo;
155
156union ExtendedFinderInfo
157{
158 ExtendedFileInfo file;
159 ExtendedFolderInfo folder;
160};
161typedef union ExtendedFinderInfo ExtendedFinderInfo;
162
163/*****************************************************************************/
164
165#pragma mark ----- GetVolParmsInfoBuffer Macros -----
166
167/*
168 * Macros to get information out of GetVolParmsInfoBuffer.
169 */
170
171/* version 1 field getters */
172#define GetVolParmsInfoVersion(volParms) \
173 ((volParms)->vMVersion)
174#define GetVolParmsInfoAttrib(volParms) \
175 ((volParms)->vMAttrib)
176#define GetVolParmsInfoLocalHand(volParms) \
177 ((volParms)->vMLocalHand)
178#define GetVolParmsInfoServerAdr(volParms) \
179 ((volParms)->vMServerAdr)
180
181/* version 2 field getters (assume zero result if version < 2) */
182#define GetVolParmsInfoVolumeGrade(volParms) \
183 (((volParms)->vMVersion >= 2) ? (volParms)->vMVolumeGrade : 0)
184#define GetVolParmsInfoForeignPrivID(volParms) \
185 (((volParms)->vMVersion >= 2) ? (volParms)->vMForeignPrivID : 0)
186
187/* version 3 field getters (assume zero result if version < 3) */
188#define GetVolParmsInfoExtendedAttributes(volParms) \
189 (((volParms)->vMVersion >= 3) ? (volParms)->vMExtendedAttributes : 0)
190
191/* attribute bits supported by all versions of GetVolParmsInfoBuffer */
192#define VolIsNetworkVolume(volParms) \
193 ((volParms)->vMServerAdr != 0)
194#define VolHasLimitFCBs(volParms) \
195 (((volParms)->vMAttrib & (1L << bLimitFCBs)) != 0)
196#define VolHasLocalWList(volParms) \
197 (((volParms)->vMAttrib & (1L << bLocalWList)) != 0)
198#define VolHasNoMiniFndr(volParms) \
199 (((volParms)->vMAttrib & (1L << bNoMiniFndr)) != 0)
200#define VolHasNoVNEdit(volParms) \
201 (((volParms)->vMAttrib & (1L << bNoVNEdit)) != 0)
202#define VolHasNoLclSync(volParms) \
203 (((volParms)->vMAttrib & (1L << bNoLclSync)) != 0)
204#define VolHasTrshOffLine(volParms) \
205 (((volParms)->vMAttrib & (1L << bTrshOffLine)) != 0)
206#define VolHasNoSwitchTo(volParms) \
207 (((volParms)->vMAttrib & (1L << bNoSwitchTo)) != 0)
208#define VolHasNoDeskItems(volParms) \
209 (((volParms)->vMAttrib & (1L << bNoDeskItems)) != 0)
210#define VolHasNoBootBlks(volParms) \
211 (((volParms)->vMAttrib & (1L << bNoBootBlks)) != 0)
212#define VolHasAccessCntl(volParms) \
213 (((volParms)->vMAttrib & (1L << bAccessCntl)) != 0)
214#define VolHasNoSysDir(volParms) \
215 (((volParms)->vMAttrib & (1L << bNoSysDir)) != 0)
216#define VolHasExtFSVol(volParms) \
217 (((volParms)->vMAttrib & (1L << bHasExtFSVol)) != 0)
218#define VolHasOpenDeny(volParms) \
219 (((volParms)->vMAttrib & (1L << bHasOpenDeny)) != 0)
220#define VolHasCopyFile(volParms) \
221 (((volParms)->vMAttrib & (1L << bHasCopyFile)) != 0)
222#define VolHasMoveRename(volParms) \
223 (((volParms)->vMAttrib & (1L << bHasMoveRename)) != 0)
224#define VolHasDesktopMgr(volParms) \
225 (((volParms)->vMAttrib & (1L << bHasDesktopMgr)) != 0)
226#define VolHasShortName(volParms) \
227 (((volParms)->vMAttrib & (1L << bHasShortName)) != 0)
228#define VolHasFolderLock(volParms) \
229 (((volParms)->vMAttrib & (1L << bHasFolderLock)) != 0)
230#define VolHasPersonalAccessPrivileges(volParms) \
231 (((volParms)->vMAttrib & (1L << bHasPersonalAccessPrivileges)) != 0)
232#define VolHasUserGroupList(volParms) \
233 (((volParms)->vMAttrib & (1L << bHasUserGroupList)) != 0)
234#define VolHasCatSearch(volParms) \
235 (((volParms)->vMAttrib & (1L << bHasCatSearch)) != 0)
236#define VolHasFileIDs(volParms) \
237 (((volParms)->vMAttrib & (1L << bHasFileIDs)) != 0)
238#define VolHasBTreeMgr(volParms) \
239 (((volParms)->vMAttrib & (1L << bHasBTreeMgr)) != 0)
240#define VolHasBlankAccessPrivileges(volParms) \
241 (((volParms)->vMAttrib & (1L << bHasBlankAccessPrivileges)) != 0)
242#define VolSupportsAsyncRequests(volParms) \
243 (((volParms)->vMAttrib & (1L << bSupportsAsyncRequests)) != 0)
244#define VolSupportsTrashVolumeCache(volParms) \
245 (((volParms)->vMAttrib & (1L << bSupportsTrashVolumeCache)) != 0)
246
247/* attribute bits supported by version 3 and greater versions of GetVolParmsInfoBuffer */
248#define VolIsEjectable(volParms) \
249 ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bIsEjectable)) != 0)
250#define VolSupportsHFSPlusAPIs(volParms) \
251 ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsHFSPlusAPIs)) != 0)
252#define VolSupportsFSCatalogSearch(volParms) \
253 ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsFSCatalogSearch)) != 0)
254#define VolSupportsFSExchangeObjects(volParms) \
255 ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsFSExchangeObjects)) != 0)
256#define VolSupports2TBFiles(volParms) \
257 ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupports2TBFiles)) != 0)
258#define VolSupportsLongNames(volParms) \
259 ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsLongNames)) != 0)
260#define VolSupportsMultiScriptNames(volParms) \
261 ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsMultiScriptNames)) != 0)
262#define VolSupportsNamedForks(volParms) \
263 ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsNamedForks)) != 0)
264#define VolSupportsSubtreeIterators(volParms) \
265 ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsSubtreeIterators)) != 0)
266#define VolL2PCanMapFileBlocks(volParms) \
267 ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bL2PCanMapFileBlocks)) != 0)
268#define VolParentModDateChanges(volParms) \
269 ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bParentModDateChanges)) != 0)
270#define VolAncestorModDateChanges(volParms) \
271 ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bAncestorModDateChanges)) != 0)
272#define VolSupportsSymbolicLinks(volParms) \
273 ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsSymbolicLinks)) != 0)
274#define VolIsAutoMounted(volParms) \
275 ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bIsAutoMounted)) != 0)
276
277/*****************************************************************************/
278
279#pragma mark ----- userPrivileges Bit Masks and Macros -----
280
281/*
282 * Bit masks and macros to get common information out of userPrivileges byte
283 * returned by FSGetCatalogInfo.
284 *
285 * Note: The userPrivileges byte is the same as the ioACUser byte returned
286 * by PBGetCatInfo, and is the 1's complement of the user's privileges
287 * byte returned in ioACAccess by PBHGetDirAccess. That's where the
288 * ioACUser names came from.
289 *
290 * The userPrivileges are user's effective privileges based on the
291 * user ID and the groups that user belongs to, and the owner, group,
292 * and everyone privileges for the given directory.
293 */
294
295enum
296{
297 /* mask for just the access restriction bits */
298 kioACUserAccessMask = (kioACUserNoSeeFolderMask +
299 kioACUserNoSeeFilesMask +
300 kioACUserNoMakeChangesMask),
301 /* common access privilege settings */
302 kioACUserFull = 0x00, /* no access restiction bits on */
303 kioACUserNone = kioACUserAccessMask, /* all access restiction bits on */
304 kioACUserDropBox = (kioACUserNoSeeFolderMask +
305 kioACUserNoSeeFilesMask), /* make changes, but not see files or folders */
306 kioACUserBulletinBoard = kioACUserNoMakeChangesMask /* see files and folders, but not make changes */
307};
308
309
310/* Macros for testing ioACUser bits. */
311
312#define UserIsOwner(userPrivileges) \
313 (((userPrivileges) & kioACUserNotOwnerMask) == 0)
314#define UserHasFullAccess(userPrivileges) \
315 (((userPrivileges) & (kioACUserAccessMask)) == kioACUserFull)
316#define UserHasDropBoxAccess(userPrivileges) \
317 (((userPrivileges) & kioACUserAccessMask) == kioACUserDropBox)
318#define UserHasBulletinBoard(userPrivileges) \
319 (((userPrivileges) & kioACUserAccessMask) == kioACUserBulletinBoard)
320#define UserHasNoAccess(userPrivileges) \
321 (((userPrivileges) & kioACUserAccessMask) == kioACUserNone)
322
323/*****************************************************************************/
324
325#pragma mark ----- File Access Routines -----
326
327/*****************************************************************************/
328
329#pragma mark FSCopyFork
330
331OSErr
332FSCopyFork(
333 SInt16 srcRefNum,
334 SInt16 dstRefNum,
335 void *copyBufferPtr,
336 ByteCount copyBufferSize);
337
338/*
339 The FSCopyFork function copies all data from the source fork to the
340 destination fork of open file forks and makes sure the destination EOF
341 is equal to the source EOF.
342
343 srcRefNum --> The source file reference number.
344 dstRefNum --> The destination file reference number.
345 copyBufferPtr --> Pointer to buffer to use during copy. The
346 buffer should be at least 4K-bytes minimum.
347 The larger the buffer, the faster the copy
348 (up to a point).
349 copyBufferSize --> The size of the copy buffer.
350*/
351
352/*****************************************************************************/
353
354#pragma mark ----- Volume Access Routines -----
355
356/*****************************************************************************/
357
358#pragma mark FSGetVolParms
359
360OSErr
361FSGetVolParms(
362 FSVolumeRefNum volRefNum,
363 UInt32 bufferSize,
364 GetVolParmsInfoBuffer *volParmsInfo,
365 UInt32 *actualInfoSize);
366
367/*
368 The FSGetVolParms function returns information about the characteristics
369 of a volume. A result of paramErr usually just means the volume doesn't
370 support GetVolParms and the feature you were going to check
371 for isn't available.
372
373 volRefNum --> Volume specification.
374 bufferSize --> Size of buffer pointed to by volParmsInfo.
375 volParmsInfo <-- A GetVolParmsInfoBuffer record where the volume
376 attributes information is returned.
377 actualInfoSize <-- The number of bytes actually returned
378 in volParmsInfo.
379
380 __________
381
382 Also see: The GetVolParmsInfoBuffer Macros for checking attribute bits
383 in this file
384*/
385
386/*****************************************************************************/
387
388#pragma mark FSGetVRefNum
389
390OSErr
391FSGetVRefNum(
392 const FSRef *ref,
393 FSVolumeRefNum *vRefNum);
394
395/*
396 The FSGetVRefNum function determines the volume reference
397 number of a volume from a FSRef.
398
399 ref --> The FSRef.
400 vRefNum <-- The volume reference number.
401*/
402
403/*****************************************************************************/
404
405#pragma mark FSGetVInfo
406
407OSErr
408FSGetVInfo(
409 FSVolumeRefNum volume,
410 HFSUniStr255 *volumeName, /* can be NULL */
411 UInt64 *freeBytes, /* can be NULL */
412 UInt64 *totalBytes); /* can be NULL */
413
414/*
415 The FSGetVInfo function returns the name, available space (in bytes),
416 and total space (in bytes) for the specified volume.
417
418 volume --> The volume reference number.
419 volumeName <** An optional pointer to a HFSUniStr255.
420 If not NULL, the volume name will be returned in
421 the HFSUniStr255.
422 freeBytes <** An optional pointer to a UInt64.
423 If not NULL, the number of free bytes on the
424 volume will be returned in the UInt64.
425 totalBytes <** An optional pointer to a UInt64.
426 If not NULL, the total number of bytes on the
427 volume will be returned in the UInt64.
428*/
429
430/*****************************************************************************/
431
432#pragma mark FSGetVolFileSystemID
433
434OSErr
435FSGetVolFileSystemID(
436 FSVolumeRefNum volume,
437 UInt16 *fileSystemID, /* can be NULL */
438 UInt16 *signature); /* can be NULL */
439
440/*
441 The FSGetVolFileSystemID function returns the file system ID and signature
442 of a mounted volume. The file system ID identifies the file system
443 that handles requests to a particular volume. The signature identifies the
444 volume type of the volume (for example, FSID 0 is Macintosh HFS Plus, HFS
445 or MFS, where a signature of 0x4244 identifies the volume as HFS).
446 Here's a partial list of file system ID numbers (only Apple's file systems
447 are listed):
448 FSID File System
449 ----- -----------------------------------------------------
450 $0000 Macintosh HFS Plus, HFS or MFS
451 $0100 ProDOS File System
452 $0101 PowerTalk Mail Enclosures
453 $4147 ISO 9660 File Access (through Foreign File Access)
454 $4242 High Sierra File Access (through Foreign File Access)
455 $464D QuickTake File System (through Foreign File Access)
456 $4953 Macintosh PC Exchange (MS-DOS)
457 $4A48 Audio CD Access (through Foreign File Access)
458 $4D4B Apple Photo Access (through Foreign File Access)
459 $6173 AppleShare (later versions of AppleShare only)
460
461 See the Technical Note "FL 35 - Determining Which File System
462 Is Active" and the "Guide to the File System Manager" for more
463 information.
464
465 volume --> The volume reference number.
466 fileSystemID <** An optional pointer to a UInt16.
467 If not NULL, the volume's file system ID will
468 be returned in the UInt16.
469 signature <** An optional pointer to a UInt16.
470 If not NULL, the volume's signature will
471 be returned in the UInt16.
472*/
473
474/*****************************************************************************/
475
476#pragma mark FSGetMountedVolumes
477
478OSErr
479FSGetMountedVolumes(
480 FSRef ***volumeRefsHandle, /* pointer to handle of FSRefs */
481 ItemCount *numVolumes);
482
483/*
484 The FSGetMountedVolumes function returns the list of volumes currently
485 mounted in an array of FSRef records. The array of FSRef records is
486 returned in a Handle, volumeRefsHandle, which is allocated by
487 FSGetMountedVolumes. The caller is responsible for disposing of
488 volumeRefsHandle if the FSGetMountedVolumes returns noErr.
489
490 volumeRefsHandle <-- Pointer to an FSRef Handle where the array of
491 FSRefs is to be returned.
492 numVolumes <-- The number of volumes returned in the array.
493*/
494
495/*****************************************************************************/
496
497#pragma mark ----- FSRef/FSpec/Path/Name Conversion Routines -----
498
499/*****************************************************************************/
500
501#pragma mark FSRefMakeFSSpec
502
503OSErr
504FSRefMakeFSSpec(
505 const FSRef *ref,
506 FSSpec *spec);
507
508/*
509 The FSRefMakeFSSpec function returns an FSSpec for the file or
510 directory specified by the ref parameter.
511
512 ref --> An FSRef specifying the file or directory.
513 spec <-- The FSSpec.
514*/
515
516/*****************************************************************************/
517
518#pragma mark FSMakeFSRef
519
520OSErr
521FSMakeFSRef(
522 FSVolumeRefNum volRefNum,
523 SInt32 dirID,
524 ConstStr255Param name,
525 FSRef *ref);
526
527/*
528 The FSMakeFSRef function creates an FSRef from the traditional
529 volume reference number, directory ID and pathname inputs. It is
530 functionally equivalent to FSMakeFSSpec followed by FSpMakeFSRef.
531
532 volRefNum --> Volume specification.
533 dirID --> Directory specification.
534 name --> The file or directory name, or NULL.
535 ref <-- The FSRef.
536*/
537
538/*****************************************************************************/
539
540#pragma mark FSMakePath
541
542OSStatus
543FSMakePath(
544 SInt16 vRefNum,
545 SInt32 dirID,
546 ConstStr255Param name,
547 UInt8 *path,
548 UInt32 maxPathSize);
549
550/*
551 The FSMakePath function creates a pathname from the traditional volume reference
552 number, directory ID, and pathname inputs. It is functionally equivalent to
553 FSMakeFSSpec, FSpMakeFSRef, FSRefMakePath.
554
555 volRefNum --> Volume specification.
556 dirID --> Directory specification.
557 name --> The file or directory name, or NULL.
558 path <-- A pointer to a buffer which FSMakePath will
559 fill with a C string representing the pathname
560 to the file or directory specified. The format of
561 the pathname returned can be determined with the
562 Gestalt selector gestaltFSAttr's
563 gestaltFSUsesPOSIXPathsForConversion bit.
564 If the gestaltFSUsesPOSIXPathsForConversion bit is
565 clear, the pathname is a Mac OS File Manager full
566 pathname in a C string, and file or directory names
567 in the pathname may be mangled as returned by
568 the File Manager. If the
569 gestaltFSUsesPOSIXPathsForConversion bit is set,
570 the pathname is a UTF8 encoded POSIX absolute
571 pathname in a C string. In either case, the
572 pathname returned can be passed back to
573 FSPathMakeRef to create an FSRef to the file or
574 directory, or FSPathMakeFSSpec to craete an FSSpec
575 to the file or directory.
576 maxPathSize --> The size of the path buffer in bytes. If the path
577 buffer is too small for the pathname string,
578 FSMakePath returns pathTooLongErr or
579 buffersTooSmall.
580*/
581
582/*****************************************************************************/
583
584#pragma mark FSPathMakeFSSpec
585
586OSStatus
587FSPathMakeFSSpec(
588 const UInt8 *path,
589 FSSpec *spec,
590 Boolean *isDirectory); /* can be NULL */
591
592/*
593 The FSPathMakeFSSpec function converts a pathname to an FSSpec.
594
595 path --> A pointer to a C String that is the pathname. The
596 format of the pathname you must supply can be
597 determined with the Gestalt selector gestaltFSAttr's
598 gestaltFSUsesPOSIXPathsForConversion bit.
599 If the gestaltFSUsesPOSIXPathsForConversion bit is
600 clear, the pathname must be a Mac OS File Manager
601 full pathname in a C string. If the
602 gestaltFSUsesPOSIXPathsForConversion bit is set,
603 the pathname must be a UTF8 encoded POSIX absolute
604 pathname in a C string.
605 spec <-- The FSSpec.
606 isDirectory <** An optional pointer to a Boolean.
607 If not NULL, true will be returned in the Boolean
608 if the specified path is a directory, or false will
609 be returned in the Boolean if the specified path is
610 a file.
611*/
612
613/*****************************************************************************/
614
615#pragma mark UnicodeNameGetHFSName
616
617OSErr
618UnicodeNameGetHFSName(
619 UniCharCount nameLength,
620 const UniChar *name,
621 TextEncoding textEncodingHint,
622 Boolean isVolumeName,
623 Str31 hfsName);
624
625/*
626 The UnicodeNameGetHFSName function converts a Unicode string
627 to a Pascal Str31 (or Str27) string using an algorithm similar to that used
628 by the File Manager. Note that if the name is too long or cannot be converted
629 using the given text encoding hint, you will get an error instead of the
630 mangled name that the File Manager would return.
631
632 nameLength --> Number of UniChar in name parameter.
633 name --> The Unicode string to convert.
634 textEncodingHint --> The text encoding hint used for the conversion.
635 You can pass kTextEncodingUnknown to use the
636 "default" textEncodingHint.
637 isVolumeName --> If true, the output name will be limited to
638 27 characters (kHFSMaxVolumeNameChars). If false,
639 the output name will be limited to 31 characters
640 (kHFSMaxFileNameChars).
641 hfsName <-- The hfsName as a Pascal string.
642
643 __________
644
645 Also see: HFSNameGetUnicodeName
646*/
647
648/*****************************************************************************/
649
650#pragma mark HFSNameGetUnicodeName
651
652OSErr
653HFSNameGetUnicodeName(
654 ConstStr31Param hfsName,
655 TextEncoding textEncodingHint,
656 HFSUniStr255 *unicodeName);
657
658/*
659 The HFSNameGetUnicodeName function converts a Pascal Str31 string to an
660 Unicode HFSUniStr255 string using the same routines as the File Manager.
661
662 hfsName --> The Pascal string to convert.
663 textEncodingHint --> The text encoding hint used for the conversion.
664 You can pass kTextEncodingUnknown to use the
665 "default" textEncodingHint.
666 unicodeName <-- The Unicode string.
667
668 __________
669
670 Also see: UnicodeNameGetHFSName
671*/
672
673/*****************************************************************************/
674
675#pragma mark ----- File/Directory Manipulation Routines -----
676
677/*****************************************************************************/
678
679#pragma mark FSRefValid
680
681Boolean FSRefValid(const FSRef *ref);
682
683/*
684 The FSRefValid function determines if an FSRef is valid. If the result is
685 true, then the FSRef refers to an existing file or directory.
686
687 ref --> FSRef to a file or directory.
688*/
689
690/*****************************************************************************/
691
692#pragma mark FSGetParentRef
693
694OSErr
695FSGetParentRef(
696 const FSRef *ref,
697 FSRef *parentRef);
698
699/*
700 The FSGetParentRef function gets the parent directory FSRef of the
701 specified object.
702
703 Note: FSRefs always point to real file system objects. So, there cannot
704 be a FSRef to the parent of volume root directories. If you call
705 FSGetParentRef with a ref to the root directory of a volume, the
706 function result will be noErr and the parentRef will be invalid (using it
707 for other file system requests will fail).
708
709 ref --> FSRef to a file or directory.
710 parentRef <-- The parent directory's FSRef.
711*/
712
713/*****************************************************************************/
714
715#pragma mark FSGetFileDirName
716
717OSErr
718FSGetFileDirName(
719 const FSRef *ref,
720 HFSUniStr255 *outName);
721
722/*
723 The FSGetFileDirName function gets the name of the file or directory
724 specified.
725
726 ref --> FSRef to a file or directory.
727 outName <-- The file or directory name.
728*/
729
730/*****************************************************************************/
731
732#pragma mark FSGetNodeID
733
734OSErr
735FSGetNodeID(
736 const FSRef *ref,
737 long *nodeID, /* can be NULL */
738 Boolean *isDirectory); /* can be NULL */
739
740/*
741 The GetNodeIDFromFSRef function gets the node ID number of the
742 file or directory specified (note: the node ID is the directory ID
743 for directories).
744
745 ref --> FSRef to a file or directory.
746 nodeID <** An optional pointer to a long.
747 If not NULL, the node ID will be returned in
748 the long.
749 isDirectory <** An optional pointer to a Boolean.
750 If not NULL, true will be returned in the Boolean
751 if the object is a directory, or false will be
752 returned in the Boolean if object is a file.
753*/
754
755/*****************************************************************************/
756
757#pragma mark FSGetUserPrivilegesPermissions
758
759OSErr
760FSGetUserPrivilegesPermissions(
761 const FSRef *ref,
762 UInt8 *userPrivileges, /* can be NULL */
763 UInt32 permissions[4]); /* can be NULL */
764
765/*
766 The FSGetUserPrivilegesPermissions function gets the userPrivileges and/or
767 permissions of the file or directory specified.
768
769 ref --> FSRef to a file or directory.
770 userPrivileges <** An optional pointer to a UInt8.
771 If not NULL, the userPrivileges will be returned
772 in the UInt8.
773 permissions <** An optional pointer to an UInt32[4] array.
774 If not NULL, the permissions will be returned
775 in the UInt32[4] array.
776*/
777
778/*****************************************************************************/
779
780#pragma mark FSCheckLock
781
782OSErr
783FSCheckLock(
784 const FSRef *ref);
785
786/*
787 The FSCheckLock function determines if a file or directory is locked.
788 If FSCheckLock returns noErr, then the file or directory is not locked
789 and the volume it is on is not locked either. If FSCheckLock returns
790 fLckdErr, then the file or directory is locked. If FSCheckLock returns
791 wPrErr, then the volume is locked by hardware (i.e., locked tab on
792 removable media). If FSCheckLock returns vLckdErr, then the volume is
793 locked by software.
794
795 ref --> FSRef to a file or directory.
796*/
797
798/*****************************************************************************/
799
800#pragma mark FSGetForkSizes
801
802OSErr
803FSGetForkSizes(
804 const FSRef *ref,
805 UInt64 *dataLogicalSize, /* can be NULL */
806 UInt64 *rsrcLogicalSize); /* can be NULL */
807
808/*
809 The FSGetForkSizes returns the size of the data and/or resource fork for
810 the specified file.
811
812 ref --> FSRef to a file or directory.
813 dataLogicalSize <** An optional pointer to a UInt64.
814 If not NULL, the data fork's size will be
815 returned in the UInt64.
816 rsrcLogicalSize <** An optional pointer to a UInt64.
817 If not NULL, the resource fork's size will be
818 returned in the UInt64.
819
820 __________
821
822 Also see: FSGetTotalForkSizes
823*/
824
825/*****************************************************************************/
826
827#pragma mark FSGetTotalForkSizes
828
829OSErr
830FSGetTotalForkSizes(
831 const FSRef *ref,
832 UInt64 *totalLogicalSize, /* can be NULL */
833 UInt64 *totalPhysicalSize, /* can be NULL */
834 ItemCount *forkCount); /* can be NULL */
835
836/*
837 The FSGetTotalForkSizes returns the total logical size and/or the total
838 physical size of the specified file (i.e., it adds the sizes of all file
839 forks). It optionally returns the number of file forks.
840
841 ref --> FSRef to a file or directory.
842 totalLogicalSize <** An optional pointer to a UInt64.
843 If not NULL, the sum of all fork logical sizes
844 will be returned in the UInt64.
845 totalPhysicalSize <** An optional pointer to a UInt64.
846 If not NULL, the sum of all fork physical sizes
847 will be returned in the UInt64.
848 forkCount <** An optional pointer to a ItemCount.
849 If not NULL, the number of file forks
850 will be returned in the ItemCount.
851
852 __________
853
854 Also see: FSGetForkSizes
855*/
856
857/*****************************************************************************/
858
859#pragma mark FSBumpDate
860
861OSErr
862FSBumpDate(
863 const FSRef *ref);
864
865/*
866 The FSBumpDate function changes the content modification date of a file
867 or directory to the current date/time. If the content modification date
868 is already equal to the current date/time, then add one second to the
869 content modification date.
870
871 ref --> FSRef to a file or directory.
872*/
873
874/*****************************************************************************/
875
876#pragma mark FSGetFinderInfo
877
878OSErr
879FSGetFinderInfo(
880 const FSRef *ref,
881 FinderInfo *info, /* can be NULL */
882 ExtendedFinderInfo *extendedInfo, /* can be NULL */
883 Boolean *isDirectory); /* can be NULL */
884
885/*
886 The FSGetFinderInfo function gets the finder information for a file or
887 directory.
888
889 ref --> FSRef to a file or directory.
890 info <** An optional pointer to a FinderInfo.
891 If not NULL, the FileInfo (if ref is a file) or
892 the FolderInfo (if ref is a folder) will be
893 returned in the FinderInfo.
894 extendedInfo <** An optional pointer to a ExtendedFinderInfo.
895 If not NULL, the ExtendedFileInfo (if ref is a file)
896 or the ExtendedFolderInfo (if ref is a folder) will
897 be returned in the ExtendedFinderInfo.
898 isDirectory <** An optional pointer to a Boolean.
899 If not NULL, true will be returned in the Boolean
900 if the object is a directory, or false will be
901 returned in the Boolean if object is a file.
902
903 __________
904
905 Also see: FSSetFinderInfo
906*/
907
908/*****************************************************************************/
909
910#pragma mark FSSetFinderInfo
911
912OSErr
913FSSetFinderInfo(
914 const FSRef *ref,
915 const FinderInfo *info, /* can be NULL */
916 const ExtendedFinderInfo *extendedInfo); /* can be NULL */
917
918/*
919 The FSSetFinderInfo function sets the finder information for a file or
920 directory.
921
922 ref --> FSRef to a file or directory.
923 info **> A pointer to a FinderInfo record with the new
924 FileInfo (if ref is a file) or new FolderInfo
925 (if ref is a folder), or NULL if the FinderInfo
926 is not to be changed.
927 extendedInfo **> A pointer to a FinderInfo record with the new
928 ExtendedFileInfo (if ref is a file) or new
929 ExtendedFolderInfo (if ref is a folder), or NULL
930 if the ExtendedFinderInfo is not to be changed.
931
932 __________
933
934 Also see: FSGetFinderInfo
935*/
936
937/*****************************************************************************/
938
939#pragma mark FSChangeCreatorType
940
941OSErr
942FSChangeCreatorType(
943 const FSRef *ref,
944 OSType fileCreator,
945 OSType fileType);
946
947/*
948 The FSChangeCreatorType function changes the creator and/or file type of a file.
949
950 ref --> FSRef to a file.
951 creator --> The new creator type or 0x00000000 to leave
952 the creator type alone.
953 fileType --> The new file type or 0x00000000 to leave the
954 file type alone.
955*/
956
957/*****************************************************************************/
958
959#pragma mark FSChangeFinderFlags
960
961OSErr
962FSChangeFinderFlags(
963 const FSRef *ref,
964 Boolean setBits,
965 UInt16 flagBits);
966
967/*
968 The FSChangeFinderFlags function sets or clears flag bits in
969 the finderFlags field of a file's FileInfo record or a
970 directory's FolderInfo record.
971
972 ref --> FSRef to a file or directory.
973 setBits --> If true, then set the bits specified in flagBits.
974 If false, then clear the bits specified in flagBits.
975 flagBits --> The flagBits parameter specifies which Finder Flag
976 bits to set or clear. If a bit in flagBits is set,
977 then the same bit in fdFlags is either set or
978 cleared depending on the state of the setBits
979 parameter.
980*/
981
982/*****************************************************************************/
983
984#pragma mark FSSetInvisible
985
986OSErr
987FSSetInvisible(
988 const FSRef *ref);
989
990#pragma mark FSClearInvisible
991
992OSErr
993FSClearInvisible(
994 const FSRef *ref);
995
996/*
997 The FSSetInvisible and FSClearInvisible functions set or clear the
998 kIsInvisible bit in the finderFlags field of the specified file or
999 directory's finder information.
1000
1001 ref --> FSRef to a file or directory.
1002*/
1003
1004/*****************************************************************************/
1005
1006#pragma mark FSSetNameLocked
1007
1008OSErr
1009FSSetNameLocked(
1010 const FSRef *ref);
1011
1012#pragma mark FSClearNameLocked
1013
1014OSErr
1015FSClearNameLocked(
1016 const FSRef *ref);
1017
1018/*
1019 The FSSetNameLocked and FSClearNameLocked functions set or clear the
1020 kNameLocked bit bit in the finderFlags field of the specified file or
1021 directory's finder information.
1022
1023 ref --> FSRef to a file or directory.
1024*/
1025
1026/*****************************************************************************/
1027
1028#pragma mark FSSetIsStationery
1029
1030OSErr
1031FSSetIsStationery(
1032 const FSRef *ref);
1033
1034#pragma mark FSClearIsStationery
1035
1036OSErr
1037FSClearIsStationery(
1038 const FSRef *ref);
1039
1040/*
1041 The FSSetIsStationery and FSClearIsStationery functions set or clear the
1042 kIsStationery bit bit in the finderFlags field of the specified file or
1043 directory's finder information.
1044
1045 ref --> FSRef to a file or directory.
1046*/
1047
1048/*****************************************************************************/
1049
1050#pragma mark FSSetHasCustomIcon
1051
1052OSErr
1053FSSetHasCustomIcon(
1054 const FSRef *ref);
1055
1056#pragma mark FSClearHasCustomIcon
1057
1058OSErr
1059FSClearHasCustomIcon(
1060 const FSRef *ref);
1061
1062/*
1063 The FSSetHasCustomIcon and FSClearHasCustomIcon functions set or clear the
1064 kHasCustomIcon bit bit in the finderFlags field of the specified file or
1065 directory's finder information.
1066
1067 ref --> FSRef to a file or directory.
1068*/
1069
1070/*****************************************************************************/
1071
1072#pragma mark FSClearHasBeenInited
1073
1074OSErr
1075FSClearHasBeenInited(
1076 const FSRef *ref);
1077
1078/*
1079 The FSClearHasBeenInited function clears the kHasBeenInited bit in the
1080 finderFlags field of the specified file or directory's finder information.
1081
1082 Note: There is no FSSetHasBeenInited function because ONLY the Finder
1083 should set the kHasBeenInited bit.
1084
1085 ref --> FSRef to a file or directory.
1086*/
1087
1088/*****************************************************************************/
1089
1090#pragma mark FSCopyFileMgrAttributes
1091
1092OSErr
1093FSCopyFileMgrAttributes(
1094 const FSRef *sourceRef,
1095 const FSRef *destinationRef,
1096 Boolean copyLockBit);
1097
1098/*
1099 The CopyFileMgrAttributes function copies all File Manager attributes
1100 from the source file or directory to the destination file or directory.
1101 If copyLockBit is true, then set the locked state of the destination
1102 to match the source.
1103
1104 sourceRef --> FSRef to a file or directory.
1105 destinationRef --> FSRef to a file or directory.
1106 copyLockBit --> If true, set the locked state of the destination
1107 to match the source.
1108*/
1109
1110/*****************************************************************************/
1111
1112#pragma mark FSMoveRenameObjectUnicode
1113
1114OSErr
1115FSMoveRenameObjectUnicode(
1116 const FSRef *ref,
1117 const FSRef *destDirectory,
1118 UniCharCount nameLength,
1119 const UniChar *name, /* can be NULL (no rename during move) */
1120 TextEncoding textEncodingHint,
1121 FSRef *newRef); /* if function fails along the way, newRef is final location of file */
1122
1123/*
1124 The FSMoveRenameObjectUnicode function moves a file or directory and
1125 optionally renames it. The source and destination locations must be on
1126 the same volume.
1127
1128 Note: If the input ref parameter is invalid, this call will fail and
1129 newRef, like ref, will be invalid.
1130
1131 ref --> FSRef to a file or directory.
1132 destDirectory --> FSRef to the destination directory.
1133 nameLength --> Number of UniChar in name parameter.
1134 name --> An Unicode string with the new name for the
1135 moved object, or NULL if no rename is wanted.
1136 textEncodingHint --> The text encoding hint used for the rename.
1137 You can pass kTextEncodingUnknown to use the
1138 "default" textEncodingHint.
1139 newRef <-- The new FSRef of the object moved. Note that if
1140 this function fails at any step along the way,
1141 newRef is still then final location of the object.
1142*/
1143
1144/*****************************************************************************/
1145
1146#pragma mark FSDeleteContainerContents
1147
1148OSErr
1149FSDeleteContainerContents(
1150 const FSRef *container);
1151
1152/*
1153 The FSDeleteContainerContents function deletes the contents of a container
1154 directory. All files and subdirectories in the specified container are
1155 deleted. If a locked file or directory is encountered, it is unlocked and
1156 then deleted. If any unexpected errors are encountered,
1157 FSDeleteContainerContents quits and returns to the caller.
1158
1159 container --> FSRef to a directory.
1160
1161 __________
1162
1163 Also see: FSDeleteContainer
1164*/
1165
1166/*****************************************************************************/
1167
1168#pragma mark FSDeleteContainer
1169
1170OSErr
1171FSDeleteContainer(
1172 const FSRef *container);
1173
1174/*
1175 The FSDeleteContainer function deletes a container directory and its contents.
1176 All files and subdirectories in the specified container are deleted.
1177 If a locked file or directory is encountered, it is unlocked and then
1178 deleted. After deleting the container's contents, the container is
1179 deleted. If any unexpected errors are encountered, FSDeleteContainer
1180 quits and returns to the caller.
1181
1182 container --> FSRef to a directory.
1183
1184 __________
1185
1186 Also see: FSDeleteContainerContents
1187*/
1188
1189/*****************************************************************************/
1190
1191#pragma mark IterateContainerFilterProcPtr
1192
1193typedef CALLBACK_API( Boolean , IterateContainerFilterProcPtr ) (
1194 Boolean containerChanged,
1195 ItemCount currentLevel,
1196 const FSCatalogInfo *catalogInfo,
1197 const FSRef *ref,
1198 const FSSpec *spec,
1199 const HFSUniStr255 *name,
1200 void *yourDataPtr);
1201
1202/*
1203 This is the prototype for the IterateContainerFilterProc function which
1204 is called once for each file and directory found by FSIterateContainer.
1205 The IterateContainerFilterProc can use the read-only data it receives for
1206 whatever it wants.
1207
1208 The result of the IterateContainerFilterProc function indicates if
1209 iteration should be stopped. To stop iteration, return true; to continue
1210 iteration, return false.
1211
1212 The yourDataPtr parameter can point to whatever data structure you might
1213 want to access from within the IterateContainerFilterProc.
1214
1215 containerChanged --> Set to true if the container's contents changed
1216 during iteration.
1217 currentLevel --> The current recursion level into the container.
1218 1 = the container, 2 = the container's immediate
1219 subdirectories, etc.
1220 catalogInfo --> The catalog information for the current object.
1221 Only the fields requested by the whichInfo
1222 parameter passed to FSIterateContainer are valid.
1223 ref --> The FSRef to the current object.
1224 spec --> The FSSpec to the current object if the wantFSSpec
1225 parameter passed to FSIterateContainer is true.
1226 name --> The name of the current object if the wantName
1227 parameter passed to FSIterateContainer is true.
1228 yourDataPtr --> An optional pointer to whatever data structure you
1229 might want to access from within the
1230 IterateFilterProc.
1231 result <-- To stop iteration, return true; to continue
1232 iteration, return false.
1233
1234 __________
1235
1236 Also see: FSIterateContainer
1237*/
1238
1239/*****************************************************************************/
1240
1241#pragma mark CallIterateContainerFilterProc
1242
1243#define CallIterateContainerFilterProc(userRoutine, containerChanged, currentLevel, catalogInfo, ref, spec, name, yourDataPtr) \
1244 (*(userRoutine))((containerChanged), (currentLevel), (catalogInfo), (ref), (spec), (name), (yourDataPtr))
1245
1246/*****************************************************************************/
1247
1248#pragma mark FSIterateContainer
1249
1250OSErr
1251FSIterateContainer(
1252 const FSRef *container,
1253 ItemCount maxLevels,
1254 FSCatalogInfoBitmap whichInfo,
1255 Boolean wantFSSpec,
1256 Boolean wantName,
1257 IterateContainerFilterProcPtr iterateFilter,
1258 void *yourDataPtr);
1259
1260/*
1261 The FSIterateContainer function performs a recursive iteration (scan) of the
1262 specified container directory and calls your IterateContainerFilterProc
1263 function once for each file and directory found.
1264
1265 The maxLevels parameter lets you control how deep the recursion goes.
1266 If maxLevels is 1, FSIterateContainer only scans the specified directory;
1267 if maxLevels is 2, FSIterateContainer scans the specified directory and
1268 one subdirectory below the specified directory; etc. Set maxLevels to
1269 zero to scan all levels.
1270
1271 The yourDataPtr parameter can point to whatever data structure you might
1272 want to access from within your IterateContainerFilterProc.
1273
1274 container --> The FSRef to the container directory to iterate.
1275 maxLevels --> Maximum number of directory levels to scan or
1276 zero to scan all directory levels.
1277 whichInfo --> The fields of the FSCatalogInfo you wish to get.
1278 wantFSSpec --> Set to true if you want the FSSpec to each
1279 object passed to your IterateContainerFilterProc.
1280 wantName --> Set to true if you want the name of each
1281 object passed to your IterateContainerFilterProc.
1282 iterateFilter --> A pointer to the IterateContainerFilterProc you
1283 want called once for each file and directory found
1284 by FSIterateContainer.
1285 yourDataPtr --> An optional pointer to whatever data structure you
1286 might want to access from within the
1287 IterateFilterProc.
1288*/
1289
1290/*****************************************************************************/
1291
1292#pragma mark FSGetDirectoryItems
1293
1294OSErr
1295FSGetDirectoryItems(
1296 const FSRef *container,
1297 FSRef ***refsHandle, /* pointer to handle of FSRefs */
1298 ItemCount *numRefs,
1299 Boolean *containerChanged);
1300
1301/*
1302 The FSGetDirectoryItems function returns the list of items in the specified
1303 container. The array of FSRef records is returned in a Handle, refsHandle,
1304 which is allocated by FSGetDirectoryItems. The caller is responsible for
1305 disposing of refsHandle if the FSGetDirectoryItems returns noErr.
1306
1307 container --> FSRef to a directory.
1308 refsHandle <-- Pointer to an FSRef Handle where the array of
1309 FSRefs is to be returned.
1310 numRefs <-- The number of FSRefs returned in the array.
1311 containerChanged <-- Set to true if the container changes while the
1312 list of items is being obtained.
1313*/
1314
1315/*****************************************************************************/
1316
1317#pragma mark FSExchangeObjectsCompat
1318
1319OSErr
1320FSExchangeObjectsCompat(
1321 const FSRef *sourceRef,
1322 const FSRef *destRef,
1323 FSRef *newSourceRef,
1324 FSRef *newDestRef);
1325
1326/*
1327 The FSExchangeObjectsCompat function exchanges the data between two files.
1328
1329 The FSExchangeObjectsCompat function is an enhanced version of
1330 FSExchangeObjects function. The two enhancements FSExchangeObjectsCompat
1331 provides are:
1332
1333 1, FSExchangeObjectsCompat will work on volumes which do not support
1334 FSExchangeObjects. FSExchangeObjectsCompat does this by emulating
1335 FSExchangeObjects through a series of File Manager operations. If
1336 there is a failure at any step along the way, FSExchangeObjectsCompat
1337 attempts to undo any steps already taken to leave the files in their
1338 original state in their original locations.
1339
1340 2. FSExchangeObjectsCompat returns new FSRefs to the source and
1341 destination files. Note that if this function fails at any step along
1342 the way, newSourceRef and newDestRef still give you access to the final
1343 locations of the files being exchanged -- even if they are renamed or
1344 not in their original locations.
1345
1346 sourceRef --> FSRef to the source file.
1347 destRef --> FSRef to the destination file.
1348 newSourceRef <-- The new FSRef to the source file.
1349 newDestRef <-- The new FSRef to the destination file.
1350*/
1351
1352/*****************************************************************************/
1353
1354#pragma mark ----- Shared Environment Routines -----
1355
1356/*****************************************************************************/
1357
1358#pragma mark FSLockRange
1359
1360OSErr
1361MoreFiles_FSLockRange(
1362 SInt16 refNum,
1363 SInt32 rangeLength,
1364 SInt32 rangeStart);
1365
1366/*
1367 The LockRange function locks (denies access to) a portion of a file
1368 that was opened with shared read/write permission.
1369
1370 refNum --> The file reference number of an open file.
1371 rangeLength --> The number of bytes in the range.
1372 rangeStart --> The starting byte in the range to lock.
1373
1374 __________
1375
1376 Also see: UnlockRange
1377*/
1378
1379/*****************************************************************************/
1380
1381#pragma mark FSUnlockRange
1382
1383OSErr
1384MoreFiles_FSUnlockRange(
1385 SInt16 refNum,
1386 SInt32 rangeLength,
1387 SInt32 rangeStart);
1388
1389/*
1390 The UnlockRange function unlocks (allows access to) a previously locked
1391 portion of a file that was opened with shared read/write permission.
1392
1393 refNum --> The file reference number of an open file.
1394 rangeLength --> The number of bytes in the range.
1395 rangeStart --> The starting byte in the range to unlock.
1396
1397 __________
1398
1399 Also see: LockRange
1400*/
1401
1402/*****************************************************************************/
1403
1404#pragma mark FSGetDirAccess
1405
1406OSErr
1407FSGetDirAccess(
1408 const FSRef *ref,
1409 SInt32 *ownerID, /* can be NULL */
1410 SInt32 *groupID, /* can be NULL */
1411 SInt32 *accessRights); /* can be NULL */
1412
1413/*
1414 The FSGetDirAccess function retrieves the directory access control
1415 information for a directory on a shared volume.
1416
1417 ref --> An FSRef specifying the directory.
1418 ownerID <** An optional pointer to a SInt32.
1419 If not NULL, the directory's owner ID
1420 will be returned in the SInt32.
1421 groupID <** An optional pointer to a SInt32.
1422 If not NULL, the directory's group ID, or 0
1423 if no group affiliation, will be returned in
1424 the SInt32.
1425 accessRights <** An optional pointer to a SInt32.
1426 If not NULL, the directory's access rights
1427 will be returned in the SInt32.
1428
1429 __________
1430
1431 Also see: FSSetDirAccess, FSMapID, FSMapName
1432*/
1433
1434/*****************************************************************************/
1435
1436#pragma mark FSSetDirAccess
1437
1438OSErr
1439FSSetDirAccess(
1440 const FSRef *ref,
1441 SInt32 ownerID,
1442 SInt32 groupID,
1443 SInt32 accessRights);
1444
1445/*
1446 The FSpSetDirAccess function changes the directory access control
1447 information for a directory on a shared volume. You must be the owner of
1448 a directory to change its access control information.
1449
1450 ref --> An FSRef specifying the directory.
1451 ownerID --> The directory's owner ID.
1452 groupID --> The directory's group ID or 0 if no group affiliation.
1453 accessRights --> The directory's access rights.
1454
1455 __________
1456
1457 Also see: FSGetDirAccess, FSMapID, FSMapName
1458*/
1459
1460/*****************************************************************************/
1461
1462#pragma mark FSGetVolMountInfoSize
1463
1464OSErr
1465FSGetVolMountInfoSize(
1466 FSVolumeRefNum volRefNum,
1467 SInt16 *size);
1468
1469/*
1470 The FSGetVolMountInfoSize function determines the how much space the
1471 program needs to allocate for a volume mounting information record.
1472
1473 volRefNum --> Volume specification.
1474 size <-- The space needed (in bytes) of the volume
1475 mounting information record.
1476
1477 __________
1478
1479 Also see: FSGetVolMountInfo, VolumeMount
1480*/
1481
1482/*****************************************************************************/
1483
1484#pragma mark FSGetVolMountInfo
1485
1486OSErr
1487FSGetVolMountInfo(
1488 FSVolumeRefNum volRefNum,
1489 void *volMountInfo);
1490
1491/*
1492 The FSGetVolMountInfo function retrieves a volume mounting information
1493 record containing all the information needed to mount the volume,
1494 except for passwords.
1495
1496 volRefNum --> Volume specification.
1497 volMountInfo <-- The volume mounting information.
1498
1499 __________
1500
1501 Also see: FSGetVolMountInfoSize, VolumeMount
1502*/
1503
1504/*****************************************************************************/
1505
1506#pragma mark FSVolumeMount
1507
1508OSErr
1509FSVolumeMount(
1510 const void *volMountInfo,
1511 FSVolumeRefNum *volRefNum);
1512
1513/*
1514 The VolumeMount function mounts a volume using a volume mounting
1515 information record.
1516
1517 volMountInfo --> A volume mounting information record.
1518 volRefNum <-- The volume reference number.
1519
1520 __________
1521
1522 Also see: FSGetVolMountInfoSize, FSGetVolMountInfo
1523*/
1524
1525/*****************************************************************************/
1526
1527#pragma mark FSMapID
1528
1529OSErr
1530FSMapID(
1531 FSVolumeRefNum volRefNum,
1532 SInt32 ugID,
1533 SInt16 objType,
1534 Str31 name);
1535
1536/*
1537 The FSMapID function determines the name of a user or group if you know
1538 the user or group ID.
1539
1540 volRefNum --> Volume specification.
1541 objType --> The mapping function code:
1542 kOwnerID2Name to map a user ID to a user name
1543 kGroupID2Name to map a group ID to a group name
1544 name <** An optional pointer to a buffer (minimum Str31).
1545 If not NULL, the user or group name
1546 will be returned in the buffer.
1547
1548 __________
1549
1550 Also see: FSGetDirAccess, FSSetDirAccess, FSMapName
1551*/
1552
1553/*****************************************************************************/
1554
1555#pragma mark FSMapName
1556
1557OSErr
1558FSMapName(
1559 FSVolumeRefNum volRefNum,
1560 ConstStr255Param name,
1561 SInt16 objType,
1562 SInt32 *ugID);
1563
1564/*
1565 The FSMapName function determines the user or group ID if you know the
1566 user or group name.
1567
1568 volRefNum --> Volume specification.
1569 name --> The user or group name.
1570 objType --> The mapping function code:
1571 kOwnerName2ID to map a user name to a user ID
1572 kGroupName2ID to map a user name to a group ID
1573 ugID <-- The user or group ID.
1574
1575 __________
1576
1577 Also see: FSGetDirAccess, FSSetDirAccess, FSMapID
1578*/
1579
1580/*****************************************************************************/
1581
1582#pragma mark FSCopyFile
1583
1584OSErr
1585FSCopyFile(
1586 const FSRef *srcFileRef,
1587 const FSRef *dstDirectoryRef,
1588 UniCharCount nameLength,
1589 const UniChar *copyName, /* can be NULL (no rename during copy) */
1590 TextEncoding textEncodingHint,
1591 FSRef *newRef); /* can be NULL */
1592
1593/*
1594 The FSCopyFile function duplicates a file and optionally renames it.
1595 The source and destination volumes must be on the same file server.
1596 This function instructs the server to copy the file.
1597
1598 srcFileRef --> An FSRef specifying the source file.
1599 dstDirectoryRef --> An FSRef specifying the destination directory.
1600 nameLength --> Number of UniChar in copyName parameter (ignored
1601 if copyName is NULL).
1602 copyName --> Points to the new file name if the file is to be
1603 renamed, or NULL if the file isn't to be renamed.
1604 textEncodingHint --> The text encoding hint used for the rename.
1605 You can pass kTextEncodingUnknown to use the
1606 "default" textEncodingHint.
1607 newRef <** An optional pointer to a FSRef.
1608 If not NULL, the FSRef of the duplicated file
1609 will be returned in the FSRef.
1610*/
1611
1612/*****************************************************************************/
1613
1614#pragma mark FSMoveRename
1615
1616OSErr
1617FSMoveRename(
1618 const FSRef *srcFileRef,
1619 const FSRef *dstDirectoryRef,
1620 UniCharCount nameLength,
1621 const UniChar *moveName, /* can be NULL (no rename during move) */
1622 TextEncoding textEncodingHint,
1623 FSRef *newRef); /* can be NULL */
1624
1625/*
1626 The FSMoveRename function moves a file or directory (object), and
1627 optionally renames it. The source and destination locations must be on
1628 the same shared volume.
1629
1630 srcFileRef --> An FSRef specifying the source file.
1631 dstDirectoryRef --> An FSRef specifying the destination directory.
1632 nameLength --> Number of UniChar in moveName parameter (ignored
1633 if copyName is NULL)
1634 moveName --> Points to the new object name if the object is to be
1635 renamed, or NULL if the object isn't to be renamed.
1636 textEncodingHint --> The text encoding hint used for the rename.
1637 You can pass kTextEncodingUnknown to use the
1638 "default" textEncodingHint.
1639 newRef <** An optional pointer to a FSRef.
1640 If not NULL, the FSRef of the moved object
1641 will be returned in the FSRef.
1642*/
1643
1644/*****************************************************************************/
1645
1646#pragma mark ----- File ID Routines -----
1647
1648/*****************************************************************************/
1649
1650#pragma mark FSResolveFileIDRef
1651
1652OSErr
1653FSResolveFileIDRef(
1654 FSVolumeRefNum volRefNum,
1655 SInt32 fileID,
1656 FSRef *ref);
1657
1658/*
1659 The FSResolveFileIDRef function returns an FSRef for the file with the
1660 specified file ID reference.
1661
1662 volRefNum --> Volume specification.
1663 fileID --> The file ID reference.
1664 ref <-- The FSRef for the file ID reference.
1665
1666 __________
1667
1668 Also see: FSCreateFileIDRef, FSDeleteFileIDRef
1669*/
1670
1671/*****************************************************************************/
1672
1673#pragma mark FSCreateFileIDRef
1674
1675OSErr
1676FSCreateFileIDRef(
1677 const FSRef *ref,
1678 SInt32 *fileID);
1679
1680/*
1681 The FSCreateFileIDRef function creates a file ID reference for the
1682 specified file, or if a file ID reference already exists, supplies
1683 the file ID reference and returns the result code fidExists or afpIDExists.
1684
1685 ref --> The FSRef for the file.
1686 fileID <-- The file ID reference (if result is noErr,
1687 fidExists, or afpIDExists).
1688
1689 __________
1690
1691 Also see: GetFSRefFromFileIDRef, FSDeleteFileIDRef
1692*/
1693
1694/*****************************************************************************/
1695
1696#pragma mark FSDeleteFileIDRef
1697
1698/*
1699 Why is there no FSDeleteFileIDRef routine? There are two reasons:
1700
1701 1. Since Mac OS 8.1, PBDeleteFileIDRef hasn't deleted file ID references.
1702 On HFS volumes, deleting a file ID reference breaks aliases (which
1703 use file ID references to track files as they are moved around on a
1704 volume) and file ID references are automatically deleted when the file
1705 they refer to is deleted. On HFS Plus volumes, file ID references are
1706 always created when a file is created, deleted when the file is deleted,
1707 and cannot be deleted at any other time.
1708
1709 2. PBDeleteFileIDRef causes a memory access fault under Mac OS X 10.0
1710 through 10.1.x. While this will be fixed in a future release, the
1711 implementation, like the Mac OS 8/9 implementation, does not delete
1712 file ID references.
1713
1714 __________
1715
1716 Also see: GetFSRefFromFileIDRef, FSCreateFileIDRef
1717*/
1718
1719/*****************************************************************************/
1720
1721#pragma mark ----- Utility Routines -----
1722
1723/*****************************************************************************/
1724
1725#pragma mark GetTempBuffer
1726
1727Ptr
1728GetTempBuffer(
1729 ByteCount buffReqSize,
1730 ByteCount *buffActSize);
1731
1732/*
1733 The GetTempBuffer function allocates a temporary buffer for file system
1734 operations which is at least 4K bytes and a multiple of 4K bytes.
1735
1736 buffReqSize --> Size you'd like the buffer to be.
1737 buffActSize <-- The size of the buffer allocated.
1738 function result <-- Pointer to memory allocated, or NULL if no memory
1739 was available. The caller is responsible for
1740 disposing of this buffer with DisposePtr.
1741*/
1742
1743/*****************************************************************************/
1744
1745#pragma mark FileRefNumGetFSRef
1746
1747OSErr
1748FileRefNumGetFSRef(
1749 short refNum,
1750 FSRef *ref);
1751
1752/*
1753 The FileRefNumGetFSRef function gets the FSRef of an open file.
1754
1755 refNum --> The file reference number of an open file.
1756 ref <-- The FSRef to the open file.
1757*/
1758
1759/*****************************************************************************/
1760
1761#pragma mark FSSetDefault
1762
1763OSErr
1764FSSetDefault(
1765 const FSRef *newDefault,
1766 FSRef *oldDefault);
1767
1768/*
1769 The FSSetDefault function sets the current working directory to the
1770 directory specified by newDefault. The previous current working directory
1771 is returned in oldDefault and must be used to restore the current working
1772 directory to its previous state with the FSRestoreDefault function.
1773 These two functions are designed to be used as a wrapper around
1774 Standard I/O routines where the location of the file is implied to be the
1775 current working directory. This is how you should use these functions:
1776
1777 result = FSSetDefault(&newDefault, &oldDefault);
1778 if ( noErr == result )
1779 {
1780 // call the Stdio functions like remove, rename,
1781 // fopen, freopen, etc here!
1782
1783 result = FSRestoreDefault(&oldDefault);
1784 }
1785
1786 newDefault --> An FSRef that specifies the new current working
1787 directory.
1788 oldDefault <-- The previous current working directory's FSRef.
1789
1790 __________
1791
1792 Also see: FSRestoreDefault
1793*/
1794
1795/*****************************************************************************/
1796
1797#pragma mark FSRestoreDefault
1798
1799OSErr
1800FSRestoreDefault(
1801 const FSRef *oldDefault);
1802
1803/*
1804 The FSRestoreDefault function restores the current working directory
1805 to the directory specified by oldDefault. The oldDefault parameter was
1806 previously obtained from the FSSetDefault function.
1807 These two functions are designed to be used as a wrapper around
1808 Standard I/O routines where the location of the file is implied to be the
1809 current working directory. This is how you should use these functions:
1810
1811 result = FSSetDefault(&newDefault, &oldDefault);
1812 if ( noErr == result )
1813 {
1814 // call the Stdio functions like remove, rename,
1815 // fopen, freopen, etc here!
1816
1817 result = FSRestoreDefault(&oldDefault);
1818 }
1819
1820 oldDefault --> The FSRef of the location to restore.
1821
1822 __________
1823
1824 Also see: FSSetDefault
1825*/
1826
1827/*****************************************************************************/
1828
1829#if PRAGMA_STRUCT_ALIGN
1830 #pragma options align=reset
1831#elif PRAGMA_STRUCT_PACKPUSH
1832 #pragma pack(pop)
1833#elif PRAGMA_STRUCT_PACK
1834 #pragma pack()
1835#endif
1836
1837#ifdef PRAGMA_IMPORT_OFF
1838#pragma import off
1839#elif PRAGMA_IMPORT
1840#pragma import reset
1841#endif
1842
1843#ifdef __cplusplus
1844}
1845#endif
1846
1847#endif /* __MOREFILESX__ */
1848
diff --git a/linden/indra/mac_updater/mac_updater.cpp b/linden/indra/mac_updater/mac_updater.cpp
new file mode 100644
index 0000000..25e203c
--- /dev/null
+++ b/linden/indra/mac_updater/mac_updater.cpp
@@ -0,0 +1,1106 @@
1/**
2 * @file mac_updater.cpp
3 * @brief
4 *
5 * Copyright (c) 2006-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29
30#include <stdio.h>
31#include <stdlib.h>
32//#include <direct.h>
33#include <time.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <unistd.h>
37
38#include <curl/curl.h>
39#include <pthread.h>
40
41#include "llerror.h"
42#include "lltimer.h"
43#include "lldir.h"
44#include "llfile.h"
45
46#include "llstring.h"
47
48#include <Carbon/Carbon.h>
49
50#include "MoreFilesX.h"
51#include "FSCopyObject.h"
52
53enum
54{
55 kEventClassCustom = 'Cust',
56 kEventCustomProgress = 'Prog',
57 kEventParamCustomCurValue = 'Cur ',
58 kEventParamCustomMaxValue = 'Max ',
59 kEventParamCustomText = 'Text',
60 kEventCustomDone = 'Done',
61};
62
63WindowRef gWindow = NULL;
64EventHandlerRef gEventHandler = NULL;
65OSStatus gFailure = noErr;
66Boolean gCancelled = false;
67
68char *gUserServer;
69char *gProductName;
70char gUpdateURL[2048];
71
72void *updatethreadproc(void*);
73
74pthread_t updatethread;
75
76OSStatus setProgress(int cur, int max)
77{
78 OSStatus err;
79 ControlRef progressBar = NULL;
80 ControlID id;
81
82 id.signature = 'prog';
83 id.id = 0;
84
85 err = GetControlByID(gWindow, &id, &progressBar);
86 if(err == noErr)
87 {
88 Boolean indeterminate;
89
90 if(max == 0)
91 {
92 indeterminate = true;
93 err = SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(Boolean), (Ptr)&indeterminate);
94 }
95 else
96 {
97 double percentage = (double)cur / (double)max;
98 SetControlMinimum(progressBar, 0);
99 SetControlMaximum(progressBar, 100);
100 SetControlValue(progressBar, (SInt16)(percentage * 100));
101
102 indeterminate = false;
103 err = SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(Boolean), (Ptr)&indeterminate);
104
105 Draw1Control(progressBar);
106 }
107 }
108
109 return(err);
110}
111
112OSStatus setProgressText(CFStringRef text)
113{
114 OSStatus err;
115 ControlRef progressText = NULL;
116 ControlID id;
117
118 id.signature = 'what';
119 id.id = 0;
120
121 err = GetControlByID(gWindow, &id, &progressText);
122 if(err == noErr)
123 {
124 err = SetControlData(progressText, kControlEntireControl, kControlStaticTextCFStringTag, sizeof(CFStringRef), (Ptr)&text);
125 Draw1Control(progressText);
126 }
127
128 return(err);
129}
130
131OSStatus sendProgress(long cur, long max, CFStringRef text = NULL)
132{
133 OSStatus result;
134 EventRef evt;
135
136 result = CreateEvent(
137 NULL,
138 kEventClassCustom,
139 kEventCustomProgress,
140 0,
141 kEventAttributeNone,
142 &evt);
143
144 // This event needs to be targeted at the window so it goes to the window's handler.
145 if(result == noErr)
146 {
147 EventTargetRef target = GetWindowEventTarget(gWindow);
148 result = SetEventParameter (
149 evt,
150 kEventParamPostTarget,
151 typeEventTargetRef,
152 sizeof(target),
153 &target);
154 }
155
156 if(result == noErr)
157 {
158 result = SetEventParameter (
159 evt,
160 kEventParamCustomCurValue,
161 typeLongInteger,
162 sizeof(cur),
163 &cur);
164 }
165
166 if(result == noErr)
167 {
168 result = SetEventParameter (
169 evt,
170 kEventParamCustomMaxValue,
171 typeLongInteger,
172 sizeof(max),
173 &max);
174 }
175
176 if(result == noErr)
177 {
178 if(text != NULL)
179 {
180 result = SetEventParameter (
181 evt,
182 kEventParamCustomText,
183 typeCFStringRef,
184 sizeof(text),
185 &text);
186 }
187 }
188
189 if(result == noErr)
190 {
191 // Send the event
192 PostEventToQueue(
193 GetMainEventQueue(),
194 evt,
195 kEventPriorityStandard);
196
197 }
198
199 return(result);
200}
201
202OSStatus sendDone(void)
203{
204 OSStatus result;
205 EventRef evt;
206
207 result = CreateEvent(
208 NULL,
209 kEventClassCustom,
210 kEventCustomDone,
211 0,
212 kEventAttributeNone,
213 &evt);
214
215 // This event needs to be targeted at the window so it goes to the window's handler.
216 if(result == noErr)
217 {
218 EventTargetRef target = GetWindowEventTarget(gWindow);
219 result = SetEventParameter (
220 evt,
221 kEventParamPostTarget,
222 typeEventTargetRef,
223 sizeof(target),
224 &target);
225 }
226
227 if(result == noErr)
228 {
229 // Send the event
230 PostEventToQueue(
231 GetMainEventQueue(),
232 evt,
233 kEventPriorityStandard);
234
235 }
236
237 return(result);
238}
239
240OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata)
241{
242 OSStatus result = eventNotHandledErr;
243 OSStatus err;
244 UInt32 evtClass = GetEventClass(event);
245 UInt32 evtKind = GetEventKind(event);
246
247 if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
248 {
249 HICommand cmd;
250 err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd);
251
252 if(err == noErr)
253 {
254 switch(cmd.commandID)
255 {
256 case kHICommandCancel:
257 gCancelled = true;
258// QuitAppModalLoopForWindow(gWindow);
259 result = noErr;
260 break;
261 }
262 }
263 }
264 else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomProgress))
265 {
266 // Request to update the progress dialog
267 long cur = 0;
268 long max = 0;
269 CFStringRef text = NULL;
270 (void) GetEventParameter(event, kEventParamCustomCurValue, typeLongInteger, NULL, sizeof(cur), NULL, &cur);
271 (void) GetEventParameter(event, kEventParamCustomMaxValue, typeLongInteger, NULL, sizeof(max), NULL, &max);
272 (void) GetEventParameter(event, kEventParamCustomText, typeCFStringRef, NULL, sizeof(text), NULL, &text);
273
274 err = setProgress(cur, max);
275 if(err == noErr)
276 {
277 if(text != NULL)
278 {
279 setProgressText(text);
280 }
281 }
282
283 result = noErr;
284 }
285 else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomDone))
286 {
287 // We're done. Exit the modal loop.
288 QuitAppModalLoopForWindow(gWindow);
289 result = noErr;
290 }
291
292 return(result);
293}
294
295#if 0
296size_t curl_download_callback(void *data, size_t size, size_t nmemb,
297 void *user_data)
298{
299 S32 bytes = size * nmemb;
300 char *cdata = (char *) data;
301 for (int i =0; i < bytes; i += 1)
302 {
303 gServerResponse.append(cdata[i]);
304 }
305 return bytes;
306}
307#endif
308
309int curl_progress_callback_func(void *clientp,
310 double dltotal,
311 double dlnow,
312 double ultotal,
313 double ulnow)
314{
315 int max = (int)(dltotal / 1024.0);
316 int cur = (int)(dlnow / 1024.0);
317 sendProgress(cur, max);
318
319 if(gCancelled)
320 return(1);
321
322 return(0);
323}
324
325int parse_args(int argc, char **argv)
326{
327 // Check for old-type arguments.
328 if (2 == argc)
329 {
330 gUserServer = argv[1];
331 return 0;
332 }
333
334 int j;
335
336 for (j = 1; j < argc; j++)
337 {
338 if ((!strcmp(argv[j], "-userserver")) && (++j < argc))
339 {
340 gUserServer = argv[j];
341 }
342 else if ((!strcmp(argv[j], "-name")) && (++j < argc))
343 {
344 gProductName = argv[j];
345 }
346 }
347
348 return 0;
349}
350
351int main(int argc, char **argv)
352{
353 // We assume that all the logs we're looking for reside on the current drive
354 gDirUtilp->initAppDirs("SecondLife");
355
356 /////////////////////////////////////////
357 //
358 // Process command line arguments
359 //
360 gUserServer = NULL;
361 gProductName = NULL;
362 parse_args(argc, argv);
363 if (!gUserServer)
364 {
365 llinfos << "Usage: mac_updater -userserver <server> [-name <product_name>] [-program <program_name>]" << llendl;
366 exit(1);
367 }
368 else
369 {
370 llinfos << "User server is: " << gUserServer << llendl;
371 if (gProductName)
372 {
373 llinfos << "Product name is: " << gProductName << llendl;
374 }
375 else
376 {
377 gProductName = "Second Life";
378 }
379 }
380
381 llinfos << "Starting " << gProductName << " Updater" << llendl;
382
383 // Build the URL to download the update
384 snprintf(gUpdateURL, sizeof(gUpdateURL), "http://secondlife.com/update-macos.php?userserver=%s", gUserServer);
385
386 // Real UI...
387 OSStatus err;
388 IBNibRef nib = NULL;
389
390 err = CreateNibReference(CFSTR("AutoUpdater"), &nib);
391
392 char windowTitle[MAX_PATH];
393 snprintf(windowTitle, sizeof(windowTitle), "%s Updater", gProductName);
394 CFStringRef windowTitleRef = NULL;
395 windowTitleRef = CFStringCreateWithCString(NULL, windowTitle, kCFStringEncodingUTF8);
396
397 if(err == noErr)
398 {
399 err = CreateWindowFromNib(nib, CFSTR("Updater"), &gWindow);
400 }
401
402 if (err == noErr)
403 {
404 err = SetWindowTitleWithCFString(gWindow, windowTitleRef);
405 }
406 CFRelease(windowTitleRef);
407
408 if(err == noErr)
409 {
410 // Set up an event handler for the window.
411 EventTypeSpec handlerEvents[] =
412 {
413 { kEventClassCommand, kEventCommandProcess },
414 { kEventClassCustom, kEventCustomProgress },
415 { kEventClassCustom, kEventCustomDone }
416 };
417 InstallStandardEventHandler(GetWindowEventTarget(gWindow));
418 InstallWindowEventHandler(
419 gWindow,
420 NewEventHandlerUPP(dialogHandler),
421 GetEventTypeCount (handlerEvents),
422 handlerEvents,
423 0,
424 &gEventHandler);
425 }
426
427 if(err == noErr)
428 {
429 ShowWindow(gWindow);
430 }
431
432 if(err == noErr)
433 {
434 pthread_create(&updatethread,
435 NULL,
436 &updatethreadproc,
437 NULL);
438
439 }
440
441 if(err == noErr)
442 {
443 RunAppModalLoopForWindow(gWindow);
444 }
445
446 void *threadresult;
447
448 pthread_join(updatethread, &threadresult);
449
450 if(!gCancelled && (gFailure != noErr))
451 {
452 // Something went wrong. Since we always just tell the user to download a new version, we don't really care what.
453 AlertStdCFStringAlertParamRec params;
454 SInt16 retval_mac = 1;
455 DialogRef alert = NULL;
456 OSStatus err;
457
458 params.version = kStdCFStringAlertVersionOne;
459 params.movable = false;
460 params.helpButton = false;
461 params.defaultText = (CFStringRef)kAlertDefaultOKText;
462 params.cancelText = 0;
463 params.otherText = 0;
464 params.defaultButton = 1;
465 params.cancelButton = 0;
466 params.position = kWindowDefaultPosition;
467 params.flags = 0;
468
469 err = CreateStandardAlert(
470 kAlertStopAlert,
471 CFSTR("Error"),
472 CFSTR("An error occurred while updating Second Life. Please download the latest version from www.secondlife.com."),
473 &params,
474 &alert);
475
476 if(err == noErr)
477 {
478 err = RunStandardAlert(
479 alert,
480 NULL,
481 &retval_mac);
482 }
483
484 }
485
486 // Don't dispose of things, just exit. This keeps the update thread from potentially getting hosed.
487 exit(0);
488
489 if(gWindow != NULL)
490 {
491 DisposeWindow(gWindow);
492 }
493
494 if(nib != NULL)
495 {
496 DisposeNibReference(nib);
497 }
498
499 return 0;
500}
501
502bool isDirWritable(FSRef &dir)
503{
504 bool result = false;
505
506 // Test for a writable directory by creating a directory, then deleting it again.
507 // This is kinda lame, but will pretty much always give the right answer.
508
509 OSStatus err = noErr;
510 char temp[PATH_MAX];
511
512 err = FSRefMakePath(&dir, (UInt8*)temp, sizeof(temp));
513
514 if(err == noErr)
515 {
516 temp[0] = '\0';
517 strncat(temp, "/.test_XXXXXX", sizeof(temp) - 1);
518
519 if(mkdtemp(temp) != NULL)
520 {
521 // We were able to make the directory. This means the directory is writable.
522 result = true;
523
524 // Clean up.
525 rmdir(temp);
526 }
527 }
528
529#if 0
530 // This seemed like a good idea, but won't tell us if we're on a volume mounted read-only.
531 UInt8 perm;
532 err = FSGetUserPrivilegesPermissions(&targetParentRef, &perm, NULL);
533 if(err == noErr)
534 {
535 if(perm & kioACUserNoMakeChangesMask)
536 {
537 // Parent directory isn't writable.
538 llinfos << "Target parent directory not writable." << llendl;
539 err = -1;
540 replacingTarget = false;
541 }
542 }
543#endif
544
545 return result;
546}
547
548static void utf8str_to_HFSUniStr255(HFSUniStr255 *dest, const char* src)
549{
550 LLWString wstr = utf8str_to_wstring(src);
551 llutf16string utf16str = wstring_to_utf16str(wstr);
552
553 dest->length = utf16str.size();
554 if(dest->length > 255)
555 {
556 // There's onl room for 255 chars in a HFSUniStr25..
557 // Truncate to avoid stack smaching or other badness.
558 dest->length = 255;
559 }
560 memcpy(dest->unicode, utf16str.data(), sizeof(UniChar)* dest->length);
561}
562
563int restoreObject(const char* aside, const char* target, const char* path, const char* object)
564{
565 char source[PATH_MAX];
566 char dest[PATH_MAX];
567 snprintf(source, sizeof(source), "%s/%s/%s", aside, path, object);
568 snprintf(dest, sizeof(dest), "%s/%s", target, path);
569 FSRef sourceRef;
570 FSRef destRef;
571 OSStatus err;
572 err = FSPathMakeRef((UInt8 *)source, &sourceRef, NULL);
573 if(err != noErr) return false;
574 err = FSPathMakeRef((UInt8 *)dest, &destRef, NULL);
575 if(err != noErr) return false;
576
577 llinfos << "Copying " << source << " to " << dest << llendl;
578
579 err = FSCopyObject(
580 &sourceRef,
581 &destRef,
582 0,
583 kFSCatInfoNone,
584 kDupeActionReplace,
585 NULL,
586 false,
587 false,
588 NULL,
589 NULL,
590 NULL,
591 NULL);
592
593 if(err != noErr) return false;
594 return true;
595}
596
597// Replace any mention of "Second Life" with the product name.
598void filterFile(const char* filename)
599{
600 char temp[PATH_MAX];
601 // First copy the target's version, so we can run it through sed.
602 snprintf(temp, sizeof(temp), "cp '%s' '%s.tmp'", filename, filename);
603 system(temp);
604
605 // Now run it through sed.
606 snprintf(temp, sizeof(temp),
607 "sed 's/Second Life/%s/g' '%s.tmp' > '%s'", gProductName, filename, filename);
608 system(temp);
609}
610
611void *updatethreadproc(void*)
612{
613 char tempDir[PATH_MAX] = "";
614 FSRef tempDirRef;
615 char temp[PATH_MAX];
616 char deviceNode[1024] = "";
617 FILE *downloadFile = NULL;
618 OSStatus err;
619 ProcessSerialNumber psn;
620 char target[PATH_MAX];
621 FSRef targetRef;
622 FSRef targetParentRef;
623 FSVolumeRefNum targetVol;
624 FSRef trashFolderRef, tempFolderRef;
625 Boolean replacingTarget = false;
626
627 memset(&tempDirRef, 0, sizeof(tempDirRef));
628 memset(&targetRef, 0, sizeof(targetRef));
629 memset(&targetParentRef, 0, sizeof(targetParentRef));
630
631 try
632 {
633 // Attempt to get a reference to the Second Life application bundle containing this updater.
634 // Any failures during this process will cause us to default to updating /Applications/Second Life.app
635 {
636 FSRef myBundle;
637
638 err = GetCurrentProcess(&psn);
639 if(err == noErr)
640 {
641 err = GetProcessBundleLocation(&psn, &myBundle);
642 }
643
644 if(err == noErr)
645 {
646 // Sanity check: Make sure the name of the item referenced by targetRef is "Second Life.app".
647 FSRefMakePath(&myBundle, (UInt8*)target, sizeof(target));
648
649 llinfos << "Updater bundle location: " << target << llendl;
650 }
651
652 // Our bundle should be in Second Life.app/Contents/Resources/AutoUpdater.app
653 // so we need to go up 3 levels to get the path to the main application bundle.
654 if(err == noErr)
655 {
656 err = FSGetParentRef(&myBundle, &targetRef);
657 }
658 if(err == noErr)
659 {
660 err = FSGetParentRef(&targetRef, &targetRef);
661 }
662 if(err == noErr)
663 {
664 err = FSGetParentRef(&targetRef, &targetRef);
665 }
666
667 // And once more to get the parent of the target
668 if(err == noErr)
669 {
670 err = FSGetParentRef(&targetRef, &targetParentRef);
671 }
672
673 if(err == noErr)
674 {
675 FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target));
676 llinfos << "Path to target: " << target << llendl;
677 }
678
679 // Sanity check: make sure the target is a bundle with the right identifier
680 if(err == noErr)
681 {
682 CFURLRef targetURL = NULL;
683 CFBundleRef targetBundle = NULL;
684 CFStringRef targetBundleID = NULL;
685
686 // Assume the worst...
687 err = -1;
688
689 targetURL = CFURLCreateFromFSRef(NULL, &targetRef);
690
691 if(targetURL == NULL)
692 {
693 llinfos << "Error creating target URL." << llendl;
694 }
695 else
696 {
697 targetBundle = CFBundleCreate(NULL, targetURL);
698 }
699
700 if(targetBundle == NULL)
701 {
702 llinfos << "Failed to create target bundle." << llendl;
703 }
704 else
705 {
706 targetBundleID = CFBundleGetIdentifier(targetBundle);
707 }
708
709 if(targetBundleID == NULL)
710 {
711 llinfos << "Couldn't retrieve target bundle ID." << llendl;
712 }
713 else
714 {
715 if(CFStringCompare(targetBundleID, CFSTR("com.secondlife.indra.viewer"), 0) == kCFCompareEqualTo)
716 {
717 // This is the bundle we're looking for.
718 err = noErr;
719 replacingTarget = true;
720 }
721 else
722 {
723 llinfos << "Target bundle ID mismatch." << llendl;
724 }
725 }
726
727 // Don't release targetBundleID -- since we don't retain it, it's released when targetBundle is released.
728 if(targetURL != NULL)
729 CFRelease(targetURL);
730 if(targetBundle != NULL)
731 CFRelease(targetBundle);
732
733 }
734
735 // Make sure the target's parent directory is writable.
736 if(err == noErr)
737 {
738 if(!isDirWritable(targetParentRef))
739 {
740 // Parent directory isn't writable.
741 llinfos << "Target parent directory not writable." << llendl;
742 err = -1;
743 replacingTarget = false;
744 }
745 }
746
747 if(err != noErr)
748 {
749 Boolean isDirectory;
750 llinfos << "Target search failed, defaulting to /Applications/" << gProductName << ".app." << llendl;
751
752 // Set up the parent directory
753 err = FSPathMakeRef((UInt8*)"/Applications", &targetParentRef, &isDirectory);
754 if((err != noErr) || (!isDirectory))
755 {
756 // We're so hosed.
757 llinfos << "Applications directory not found, giving up." << llendl;
758 throw 0;
759 }
760
761 snprintf(target, sizeof(target), "/Applications/%s.app", gProductName);
762
763 memset(&targetRef, 0, sizeof(targetRef));
764 err = FSPathMakeRef((UInt8*)target, &targetRef, NULL);
765 if(err == fnfErr)
766 {
767 // This is fine, just means we're not replacing anything.
768 err = noErr;
769 replacingTarget = false;
770 }
771 else
772 {
773 replacingTarget = true;
774 }
775
776 // Make sure the target's parent directory is writable.
777 if(err == noErr)
778 {
779 if(!isDirWritable(targetParentRef))
780 {
781 // Parent directory isn't writable.
782 llinfos << "Target parent directory not writable." << llendl;
783 err = -1;
784 replacingTarget = false;
785 }
786 }
787
788 }
789
790 // If we haven't fixed all problems by this point, just bail.
791 if(err != noErr)
792 {
793 llinfos << "Unable to pick a target, giving up." << llendl;
794 throw 0;
795 }
796 }
797
798 // Find the volID of the volume the target resides on
799 {
800 FSCatalogInfo info;
801 err = FSGetCatalogInfo(
802 &targetParentRef,
803 kFSCatInfoVolume,
804 &info,
805 NULL,
806 NULL,
807 NULL);
808
809 if(err != noErr)
810 throw 0;
811
812 targetVol = info.volume;
813 }
814
815 // Find the temporary items and trash folders on that volume.
816 err = FSFindFolder(
817 targetVol,
818 kTrashFolderType,
819 true,
820 &trashFolderRef);
821
822 if(err != noErr)
823 throw 0;
824
825 err = FSFindFolder(
826 targetVol,
827 kTemporaryFolderType,
828 true,
829 &tempFolderRef);
830
831 if(err != noErr)
832 throw 0;
833
834 err = FSRefMakePath(&tempFolderRef, (UInt8*)temp, sizeof(temp));
835
836 if(err != noErr)
837 throw 0;
838
839 temp[0] = '\0';
840 strncat(temp, "/SecondLifeUpdate_XXXXXX", sizeof(temp) - 1);
841 if(mkdtemp(temp) == NULL)
842 {
843 throw 0;
844 }
845
846 strcpy(tempDir, temp);
847
848 llinfos << "tempDir is " << tempDir << llendl;
849
850 err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL);
851
852 if(err != noErr)
853 throw 0;
854
855 chdir(tempDir);
856
857 snprintf(temp, sizeof(temp), "SecondLife.dmg");
858
859 downloadFile = fopen(temp, "wb");
860 if(downloadFile == NULL)
861 {
862 throw 0;
863 }
864
865 {
866 CURL *curl = curl_easy_init();
867
868 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
869 // curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback);
870 curl_easy_setopt(curl, CURLOPT_FILE, downloadFile);
871 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
872 curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &curl_progress_callback_func);
873 curl_easy_setopt(curl, CURLOPT_URL, gUpdateURL);
874 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
875
876 sendProgress(0, 1, CFSTR("Downloading..."));
877
878 CURLcode result = curl_easy_perform(curl);
879
880 curl_easy_cleanup(curl);
881
882 if(gCancelled)
883 {
884 llinfos << "User cancel, bailing out."<< llendl;
885 throw 0;
886 }
887
888 if(result != CURLE_OK)
889 {
890 llinfos << "Error " << result << " while downloading disk image."<< llendl;
891 throw 0;
892 }
893
894 fclose(downloadFile);
895 downloadFile = NULL;
896 }
897
898 sendProgress(0, 0, CFSTR("Mounting image..."));
899 LLFile::mkdir("mnt", 0700);
900
901 // NOTE: we could add -private at the end of this command line to keep the image from showing up in the Finder,
902 // but if our cleanup fails, this makes it much harder for the user to unmount the image.
903 LLString mountOutput;
904 FILE *mounter = popen("hdiutil attach SecondLife.dmg -mountpoint mnt", "r");
905
906 if(mounter == NULL)
907 {
908 llinfos << "Failed to mount disk image, exiting."<< llendl;
909 throw 0;
910 }
911
912 // We need to scan the output from hdiutil to find the device node it uses to attach the disk image.
913 // If we don't have this information, we can't detach it later.
914 while(mounter != NULL)
915 {
916 size_t len = fread(temp, 1, sizeof(temp)-1, mounter);
917 temp[len] = 0;
918 mountOutput.append(temp);
919 if(len < sizeof(temp)-1)
920 {
921 // End of file or error.
922 if(pclose(mounter) != 0)
923 {
924 llinfos << "Failed to mount disk image, exiting."<< llendl;
925 throw 0;
926 }
927 mounter = NULL;
928 }
929 }
930
931 if(!mountOutput.empty())
932 {
933 const char *s = mountOutput.c_str();
934 char *prefix = "/dev/";
935 char *sub = strstr(s, prefix);
936
937 if(sub != NULL)
938 {
939 sub += strlen(prefix);
940 sscanf(sub, "%s", deviceNode);
941 }
942 }
943
944 if(deviceNode[0] != 0)
945 {
946 llinfos << "Disk image attached on /dev/" << deviceNode << llendl;
947 }
948 else
949 {
950 llinfos << "Disk image device node not found!" << llendl;
951 }
952
953 // Get an FSRef to the new application on the disk image
954 FSRef sourceRef;
955 snprintf(temp, sizeof(temp), "%s/mnt/Second Life.app", tempDir);
956
957 llinfos << "Source application is: " << temp << llendl;
958
959 err = FSPathMakeRef((UInt8 *)temp, &sourceRef, NULL);
960 if(err != noErr)
961 throw 0;
962
963 FSRef asideRef;
964 char aside[MAX_PATH];
965
966 // this will hold the name of the destination target
967 HFSUniStr255 appNameUniStr;
968
969 if(replacingTarget)
970 {
971 // Get the name of the target we're replacing
972 err = FSGetCatalogInfo(&targetRef, 0, NULL, &appNameUniStr, NULL, NULL);
973 if(err != noErr)
974 throw 0;
975
976 // Move aside old version (into work directory)
977 err = FSMoveObject(&targetRef, &tempDirRef, &asideRef);
978 if(err != noErr)
979 throw 0;
980
981 // Grab the path for later use.
982 err = FSRefMakePath(&asideRef, (UInt8*)aside, sizeof(aside));
983 }
984 else
985 {
986 // Construct the name of the target based on the product name
987 char appName[MAX_PATH];
988 snprintf(appName, sizeof(appName), "%s.app", gProductName);
989 utf8str_to_HFSUniStr255( &appNameUniStr, appName );
990 }
991
992 sendProgress(0, 0, CFSTR("Copying files..."));
993
994 llinfos << "Starting copy..." << llendl;
995
996 // Copy the new version from the disk image to the target location.
997 err = FSCopyObject(
998 &sourceRef,
999 &targetParentRef,
1000 0,
1001 kFSCatInfoNone,
1002 kDupeActionStandard,
1003 &appNameUniStr,
1004 false,
1005 false,
1006 NULL,
1007 NULL,
1008 &targetRef,
1009 NULL);
1010
1011 // Grab the path for later use.
1012 err = FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target));
1013 if(err != noErr)
1014 throw 0;
1015
1016 llinfos << "Copy complete. Target = " << target << llendl;
1017
1018 if(err != noErr)
1019 {
1020 // Something went wrong during the copy. Attempt to put the old version back and bail.
1021 (void)FSDeleteObjects(&targetRef);
1022 if(replacingTarget)
1023 {
1024 (void)FSMoveObject(&asideRef, &targetParentRef, NULL);
1025 }
1026 throw 0;
1027 }
1028 else
1029 {
1030 // The update has succeeded. Clear the cache directory.
1031
1032 sendProgress(0, 0, CFSTR("Clearing cache..."));
1033
1034 llinfos << "Clearing cache..." << llendl;
1035
1036 char mask[LL_MAX_PATH];
1037 sprintf(mask, "%s*.*", gDirUtilp->getDirDelimiter().c_str());
1038 gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask);
1039
1040 llinfos << "Clear complete." << llendl;
1041
1042 }
1043 }
1044 catch(...)
1045 {
1046 if(!gCancelled)
1047 if(gFailure == noErr)
1048 gFailure = -1;
1049 }
1050
1051 // Failures from here on out are all non-fatal and not reported.
1052 sendProgress(0, 3, CFSTR("Cleaning up..."));
1053
1054 // Close disk image file if necessary
1055 if(downloadFile != NULL)
1056 {
1057 llinfos << "Closing download file." << llendl;
1058
1059 fclose(downloadFile);
1060 downloadFile = NULL;
1061 }
1062
1063 sendProgress(1, 3);
1064 // Unmount image
1065 if(deviceNode[0] != 0)
1066 {
1067 llinfos << "Detaching disk image." << llendl;
1068
1069 snprintf(temp, sizeof(temp), "hdiutil detach '%s'", deviceNode);
1070 system(temp);
1071 }
1072
1073 sendProgress(2, 3);
1074
1075 // Move work directory to the trash
1076 if(tempDir[0] != 0)
1077 {
1078// chdir("/");
1079// FSDeleteObjects(tempDirRef);
1080
1081 llinfos << "Moving work directory to the trash." << llendl;
1082
1083 err = FSMoveObject(&tempDirRef, &trashFolderRef, NULL);
1084
1085// snprintf(temp, sizeof(temp), "rm -rf '%s'", tempDir);
1086// printf("%s\n", temp);
1087// system(temp);
1088 }
1089
1090 if(!gCancelled && !gFailure && (target[0] != 0))
1091 {
1092 llinfos << "Touching application bundle." << llendl;
1093
1094 snprintf(temp, sizeof(temp), "touch '%s'", target);
1095 system(temp);
1096
1097 llinfos << "Launching updated application." << llendl;
1098
1099 snprintf(temp, sizeof(temp), "open '%s'", target);
1100 system(temp);
1101 }
1102
1103 sendDone();
1104
1105 return(NULL);
1106}