]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_search.c
xnu-792.17.14.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_search.c
1 /*
2 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 *
28 * @(#)hfs_search.c
29 */
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/file.h>
35 #include <sys/proc.h>
36 #include <sys/conf.h>
37 #include <mach/machine/vm_types.h>
38 #include <sys/vnode.h>
39 #include <sys/malloc.h>
40 #include <sys/signalvar.h>
41 #include <sys/attr.h>
42 #include <sys/utfconv.h>
43 #include <sys/kauth.h>
44 #include <sys/vnode_internal.h>
45
46 #include "hfs.h"
47 #include "hfs_dbg.h"
48 #include "hfs_catalog.h"
49 #include "hfs_attrlist.h"
50 #include "hfs_endian.h"
51
52 #include "hfscommon/headers/FileMgrInternal.h"
53 #include "hfscommon/headers/HFSUnicodeWrappers.h"
54 #include "hfscommon/headers/BTreesPrivate.h"
55 #include "hfscommon/headers/BTreeScanner.h"
56 #include "hfscommon/headers/CatalogPrivate.h"
57
58 /* Search criterea. */
59 struct directoryInfoSpec
60 {
61 u_int32_t numFiles;
62 };
63
64 struct fileInfoSpec
65 {
66 off_t dataLogicalLength;
67 off_t dataPhysicalLength;
68 off_t resourceLogicalLength;
69 off_t resourcePhysicalLength;
70 };
71
72 struct searchinfospec
73 {
74 u_char name[kHFSPlusMaxFileNameBytes];
75 u_long nameLength;
76 char attributes; // see IM:Files 2-100
77 u_long nodeID;
78 u_long parentDirID;
79 struct timespec creationDate;
80 struct timespec modificationDate;
81 struct timespec changeDate;
82 struct timespec accessDate;
83 struct timespec lastBackupDate;
84 uint8_t finderInfo[32];
85 uid_t uid;
86 gid_t gid;
87 mode_t mask;
88 struct fileInfoSpec f;
89 struct directoryInfoSpec d;
90 };
91 typedef struct searchinfospec searchinfospec_t;
92
93 static void ResolveHardlink(ExtendedVCB *vcb, HFSPlusCatalogFile *recp);
94
95
96 static int UnpackSearchAttributeBlock(struct hfsmount *hfsmp, struct attrlist *alist,
97 searchinfospec_t *searchInfo, void *attributeBuffer);
98
99 static int CheckCriteria( ExtendedVCB *vcb,
100 u_long searchBits,
101 struct attrlist *attrList,
102 CatalogRecord *rec,
103 CatalogKey *key,
104 searchinfospec_t *searchInfo1,
105 searchinfospec_t *searchInfo2,
106 Boolean lookForDup );
107
108 static int CheckAccess(ExtendedVCB *vcb, u_long searchBits, CatalogKey *key, struct vfs_context *ctx);
109
110 static int InsertMatch(struct hfsmount *hfsmp, uio_t a_uio, CatalogRecord *rec,
111 CatalogKey *key, struct attrlist *returnAttrList,
112 void *attributesBuffer, void *variableBuffer,
113 u_long * nummatches );
114
115 static Boolean CompareRange(u_long val, u_long low, u_long high);
116 static Boolean CompareWideRange(u_int64_t val, u_int64_t low, u_int64_t high);
117
118 static Boolean CompareRange( u_long val, u_long low, u_long high )
119 {
120 return( (val >= low) && (val <= high) );
121 }
122
123 static Boolean CompareWideRange( u_int64_t val, u_int64_t low, u_int64_t high )
124 {
125 return( (val >= low) && (val <= high) );
126 }
127 //#define CompareRange(val, low, high) ((val >= low) && (val <= high))
128
129 #if 1 // Installer workaround (2940423)
130 static Boolean IsTargetName( searchinfospec_t * searchInfoPtr, Boolean isHFSPlus );
131 #endif // Installer workaround
132
133 __private_extern__ int hfs_vnop_search(struct vnop_searchfs_args *ap);
134
135
136 /************************************************************************/
137 /* Entry for searchfs() */
138 /************************************************************************/
139
140 #define errSearchBufferFull 101 /* Internal search errors */
141 /*
142 #
143 #% searchfs vp L L L
144 #
145 vnop_searchfs {
146 IN struct vnode *vp;
147 IN off_t length;
148 IN int flags;
149 IN kauth_cred_t cred;
150 IN struct proc *p;
151 };
152 */
153
154 __private_extern__
155 int
156 hfs_vnop_search(ap)
157 struct vnop_searchfs_args *ap; /*
158 struct vnodeop_desc *a_desc;
159 struct vnode *a_vp;
160 void *a_searchparams1;
161 void *a_searchparams2;
162 struct attrlist *a_searchattrs;
163 u_long a_maxmatches;
164 struct timeval *a_timelimit;
165 struct attrlist *a_returnattrs;
166 u_long *a_nummatches;
167 u_long a_scriptcode;
168 u_long a_options;
169 struct uio *a_uio;
170 struct searchstate *a_searchstate;
171 vfs_context_t a_context;
172 */
173 {
174 ExtendedVCB *vcb = VTOVCB(ap->a_vp);
175 struct hfsmount *hfsmp;
176 FCB * catalogFCB;
177 searchinfospec_t searchInfo1;
178 searchinfospec_t searchInfo2;
179 void *attributesBuffer;
180 void *variableBuffer;
181 u_long fixedBlockSize;
182 u_long eachReturnBufferSize;
183 struct proc *p = proc_self();
184 int err = E_NONE;
185 int isHFSPlus;
186 int timerExpired = false;
187 int doQuickExit = false;
188 CatalogKey * myCurrentKeyPtr;
189 CatalogRecord * myCurrentDataPtr;
190 CatPosition * myCatPositionPtr;
191 BTScanState myBTScanState;
192 user_addr_t user_start = 0;
193 user_size_t user_len = 0;
194 int32_t searchTime;
195 int lockflags;
196
197 /* XXX Parameter check a_searchattrs? */
198
199 *(ap->a_nummatches) = 0;
200
201 if (ap->a_options & ~SRCHFS_VALIDOPTIONSMASK)
202 return (EINVAL);
203
204 /* SRCHFS_SKIPLINKS requires root access.
205 * This option cannot be used with either
206 * the ATTR_CMN_NAME or ATTR_CMN_PAROBJID
207 * attributes.
208 */
209 if (ap->a_options & SRCHFS_SKIPLINKS) {
210 attrgroup_t attrs;
211
212 attrs = ap->a_searchattrs->commonattr | ap->a_returnattrs->commonattr;
213 if (attrs & (ATTR_CMN_NAME | ATTR_CMN_PAROBJID))
214 return (EINVAL);
215 if ((err = vfs_context_suser(ap->a_context)))
216 return (err);
217 }
218
219 if (uio_resid(ap->a_uio) <= 0)
220 return (EINVAL);
221
222 isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
223 hfsmp = VTOHFS(ap->a_vp);
224
225 searchTime = kMaxMicroSecsInKernel;
226 if (ap->a_timelimit->tv_sec == 0 &&
227 ap->a_timelimit->tv_usec > 0 &&
228 ap->a_timelimit->tv_usec < kMaxMicroSecsInKernel) {
229 searchTime = ap->a_timelimit->tv_usec;
230 }
231
232 /* UnPack the search boundries, searchInfo1, searchInfo2 */
233 err = UnpackSearchAttributeBlock(hfsmp, ap->a_searchattrs,
234 &searchInfo1, ap->a_searchparams1);
235 if (err) return err;
236 err = UnpackSearchAttributeBlock(hfsmp, ap->a_searchattrs,
237 &searchInfo2, ap->a_searchparams2);
238 if (err) return err;
239
240 fixedBlockSize = sizeof(uint32_t) + hfs_attrblksize(ap->a_returnattrs); /* uint32_t for length word */
241
242 eachReturnBufferSize = fixedBlockSize;
243
244 if ( ap->a_returnattrs->commonattr & ATTR_CMN_NAME ) /* XXX should be more robust! */
245 eachReturnBufferSize += kHFSPlusMaxFileNameBytes + 1;
246
247 MALLOC( attributesBuffer, void *, eachReturnBufferSize, M_TEMP, M_WAITOK );
248 variableBuffer = (void*)((char*) attributesBuffer + fixedBlockSize);
249
250 // XXXdbg - have to lock the user's buffer so we don't fault
251 // while holding the shared catalog file lock. see the comment
252 // in hfs_readdir() for more details.
253 //
254 if (hfsmp->jnl && uio_isuserspace(ap->a_uio)) {
255 user_start = uio_curriovbase(ap->a_uio);
256 user_len = uio_curriovlen(ap->a_uio);
257
258 if ((err = vslock(user_start, user_len)) != 0) {
259 user_start = 0;
260 goto ExitThisRoutine;
261 }
262 }
263
264 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
265
266 catalogFCB = GetFileControlBlock(vcb->catalogRefNum);
267 myCurrentKeyPtr = NULL;
268 myCurrentDataPtr = NULL;
269 myCatPositionPtr = (CatPosition *)ap->a_searchstate;
270
271 if (ap->a_options & SRCHFS_START) {
272 /* Starting a new search. */
273 /* Make sure the on-disk Catalog file is current */
274 (void) hfs_fsync(vcb->catalogRefNum, MNT_WAIT, 0, p);
275 if (hfsmp->jnl) {
276 hfs_systemfile_unlock(hfsmp, lockflags);
277 journal_flush(hfsmp->jnl);
278 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
279 }
280
281 ap->a_options &= ~SRCHFS_START;
282 bzero( (caddr_t)myCatPositionPtr, sizeof( *myCatPositionPtr ) );
283 err = BTScanInitialize(catalogFCB, 0, 0, 0, kCatSearchBufferSize, &myBTScanState);
284
285 #if 1 // Installer workaround (2940423)
286 // hack to get around installer problems when the installer expects search results
287 // to be in key order. At this point the problem appears to be limited to
288 // searches for "Library". The idea here is to go get the "Library" at root
289 // and return it first to the caller then continue the search as normal with
290 // the exception of taking care not to return a duplicate hit (see CheckCriteria)
291 if ( err == E_NONE &&
292 (ap->a_searchattrs->commonattr & ATTR_CMN_NAME) != 0 &&
293 IsTargetName( &searchInfo1, isHFSPlus ) )
294 {
295 CatalogRecord rec;
296 BTreeIterator iterator;
297 FSBufferDescriptor btrec;
298 CatalogKey * keyp;
299 UInt16 reclen;
300 OSErr result;
301
302 bzero( (caddr_t)&iterator, sizeof( iterator ) );
303 keyp = (CatalogKey *) &iterator.key;
304 (void) BuildCatalogKeyUTF8(vcb, kRootDirID, "Library", kUndefinedStrLen, keyp, NULL);
305
306 btrec.bufferAddress = &rec;
307 btrec.itemCount = 1;
308 btrec.itemSize = sizeof( rec );
309
310 result = BTSearchRecord( catalogFCB, &iterator, &btrec, &reclen, &iterator );
311 if ( result == E_NONE ) {
312 // need to unlock since CheckAccess assumes no lock held
313 hfs_systemfile_unlock(hfsmp, lockflags);
314 if (CheckCriteria(vcb, ap->a_options, ap->a_searchattrs, &rec,
315 keyp, &searchInfo1, &searchInfo2, false) &&
316 CheckAccess(vcb, ap->a_options, keyp, ap->a_context)) {
317
318 result = InsertMatch(hfsmp, ap->a_uio, &rec,
319 keyp, ap->a_returnattrs,
320 attributesBuffer, variableBuffer,
321 ap->a_nummatches);
322 if (result == E_NONE && *(ap->a_nummatches) >= ap->a_maxmatches)
323 doQuickExit = true;
324 }
325 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
326 }
327 }
328 #endif // Installer workaround
329 } else {
330 /* Resuming a search. */
331 err = BTScanInitialize(catalogFCB, myCatPositionPtr->nextNode,
332 myCatPositionPtr->nextRecord,
333 myCatPositionPtr->recordsFound,
334 kCatSearchBufferSize,
335 &myBTScanState);
336 /* Make sure Catalog hasn't changed. */
337 if (err == 0
338 && myCatPositionPtr->writeCount != myBTScanState.btcb->writeCount) {
339 myCatPositionPtr->writeCount = myBTScanState.btcb->writeCount;
340 err = EBUSY; /* catChangedErr */
341 }
342 }
343 hfs_systemfile_unlock(hfsmp, lockflags);
344
345 if (err)
346 goto ExitThisRoutine;
347 #if 1 // Installer workaround (2940423)
348 if ( doQuickExit )
349 goto QuickExit;
350 #endif // Installer workaround
351
352 /*
353 * Check all the catalog btree records...
354 * return the attributes for matching items
355 */
356 for (;;) {
357 struct timeval myCurrentTime;
358 struct timeval myElapsedTime;
359
360 err = BTScanNextRecord(&myBTScanState, timerExpired,
361 (void **)&myCurrentKeyPtr, (void **)&myCurrentDataPtr,
362 NULL);
363 if (err)
364 break;
365
366 /* Resolve any hardlinks */
367 if (isHFSPlus && (ap->a_options & SRCHFS_SKIPLINKS) == 0) {
368 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
369 ResolveHardlink(vcb, (HFSPlusCatalogFile *) myCurrentDataPtr);
370 hfs_systemfile_unlock(hfsmp, lockflags);
371 }
372 if (CheckCriteria( vcb, ap->a_options, ap->a_searchattrs, myCurrentDataPtr,
373 myCurrentKeyPtr, &searchInfo1, &searchInfo2, true )
374 && CheckAccess(vcb, ap->a_options, myCurrentKeyPtr, ap->a_context)) {
375 err = InsertMatch(hfsmp, ap->a_uio, myCurrentDataPtr,
376 myCurrentKeyPtr, ap->a_returnattrs,
377 attributesBuffer, variableBuffer, ap->a_nummatches);
378 if (err) {
379 /*
380 * The last match didn't fit so come back
381 * to this record on the next trip.
382 */
383 --myBTScanState.recordsFound;
384 --myBTScanState.recordNum;
385 break;
386 }
387
388 if (*(ap->a_nummatches) >= ap->a_maxmatches)
389 break;
390 }
391
392 /*
393 * Check our elapsed time and bail if we've hit the max.
394 * The idea here is to throttle the amount of time we
395 * spend in the kernel.
396 */
397 microuptime(&myCurrentTime);
398 timersub(&myCurrentTime, &myBTScanState.startTime, &myElapsedTime);
399 /* Note: assumes kMaxMicroSecsInKernel is less than 1,000,000 */
400 if (myElapsedTime.tv_sec > 0
401 || myElapsedTime.tv_usec >= searchTime) {
402 timerExpired = true;
403 }
404 }
405
406 QuickExit:
407 /* Update catalog position */
408 myCatPositionPtr->writeCount = myBTScanState.btcb->writeCount;
409
410 BTScanTerminate(&myBTScanState, &myCatPositionPtr->nextNode,
411 &myCatPositionPtr->nextRecord,
412 &myCatPositionPtr->recordsFound);
413
414 if ( err == E_NONE ) {
415 err = EAGAIN; /* signal to the user to call searchfs again */
416 } else if ( err == errSearchBufferFull ) {
417 if ( *(ap->a_nummatches) > 0 )
418 err = EAGAIN;
419 else
420 err = ENOBUFS;
421 } else if ( err == btNotFound ) {
422 err = E_NONE; /* the entire disk has been searched */
423 } else if ( err == fsBTTimeOutErr ) {
424 err = EAGAIN;
425 }
426
427 ExitThisRoutine:
428 FREE( attributesBuffer, M_TEMP );
429
430 if (hfsmp->jnl && user_start) {
431 vsunlock(user_start, user_len, TRUE);
432 }
433
434 return (MacToVFSError(err));
435 }
436
437
438 static void
439 ResolveHardlink(ExtendedVCB *vcb, HFSPlusCatalogFile *recp)
440 {
441 if ((recp->recordType == kHFSPlusFileRecord)
442 && (SWAP_BE32(recp->userInfo.fdType) == kHardLinkFileType)
443 && (SWAP_BE32(recp->userInfo.fdCreator) == kHFSPlusCreator)
444 && ((to_bsd_time(recp->createDate) == vcb->vcbCrDate) ||
445 (to_bsd_time(recp->createDate) == VCBTOHFS(vcb)->hfs_metadata_createdate))) {
446 cnid_t saved_cnid;
447
448 /* Export link's cnid (a unique value) instead of inode's cnid */
449 saved_cnid = recp->fileID;
450 (void) resolvelink(VCBTOHFS(vcb), recp->bsdInfo.special.iNodeNum, recp);
451 recp->fileID = saved_cnid;
452 }
453 }
454
455
456 static Boolean
457 CompareMasked(const UInt32 *thisValue, const UInt32 *compareData,
458 const UInt32 *compareMask, UInt32 count)
459 {
460 Boolean matched;
461 UInt32 i;
462
463 matched = true; /* Assume it will all match */
464
465 for (i=0; i<count; i++) {
466 if (((*thisValue++ ^ *compareData++) & *compareMask++) != 0) {
467 matched = false;
468 break;
469 }
470 }
471
472 return matched;
473 }
474
475
476 static Boolean
477 ComparePartialUnicodeName (register ConstUniCharArrayPtr str, register ItemCount s_len,
478 register ConstUniCharArrayPtr find, register ItemCount f_len )
479 {
480 if (f_len == 0 || s_len == 0)
481 return FALSE;
482
483 do {
484 if (s_len-- < f_len)
485 return FALSE;
486 } while (FastUnicodeCompare(str++, f_len, find, f_len) != 0);
487
488 return TRUE;
489 }
490
491
492 static Boolean
493 ComparePartialPascalName ( register ConstStr31Param str, register ConstStr31Param find )
494 {
495 register u_char s_len = str[0];
496 register u_char f_len = find[0];
497 register u_char *tsp;
498 Str31 tmpstr;
499
500 if (f_len == 0 || s_len == 0)
501 return FALSE;
502
503 bcopy(str, tmpstr, s_len + 1);
504 tsp = &tmpstr[0];
505
506 while (s_len-- >= f_len) {
507 *tsp = f_len;
508
509 if (FastRelString(tsp++, find) == 0)
510 return TRUE;
511 }
512
513 return FALSE;
514 }
515
516
517 //
518 // Determine if a name is "inappropriate" where the definition
519 // of "inappropriate" is up to higher level execs. Currently
520 // that's limited to /System.
521 //
522 static int
523 is_inappropriate_name(char *name, int len)
524 {
525 const char *bad_names[] = { "System" };
526 int bad_len[] = { 6 };
527 int i;
528
529 for(i=0; i < (int) (sizeof(bad_names) / sizeof(bad_names[0])); i++) {
530 if (len == bad_len[i] && strcmp(name, bad_names[i]) == 0) {
531 return 1;
532 }
533 }
534
535 // if we get here, no name matched
536 return 0;
537 }
538
539
540
541 /*
542 * Check to see if caller has access rights to this item
543 */
544
545 static int
546 CheckAccess(ExtendedVCB *theVCBPtr, u_long searchBits, CatalogKey *theKeyPtr, struct vfs_context *ctx)
547 {
548 Boolean isHFSPlus;
549 int myErr;
550 int myResult;
551 HFSCatalogNodeID myNodeID;
552 hfsmount_t * hfsmp;
553 struct FndrDirInfo *finfop;
554 struct vnode * vp = NULL;
555
556 myResult = 0; /* default to "no access" */
557
558 if (!vfs_context_suser(ctx)) {
559 myResult = 1; /* allow access */
560 goto ExitThisRoutine; /* root always has access */
561 }
562
563 hfsmp = VCBTOHFS( theVCBPtr );
564 isHFSPlus = ( theVCBPtr->vcbSigWord == kHFSPlusSigWord );
565 if ( isHFSPlus )
566 myNodeID = theKeyPtr->hfsPlus.parentID;
567 else
568 myNodeID = theKeyPtr->hfs.parentID;
569
570 while ( myNodeID >= kRootDirID ) {
571 cnode_t * cp;
572
573 /* now go get catalog data for this directory */
574 myErr = hfs_vget(hfsmp, myNodeID, &vp, 0);
575 if ( myErr ) {
576 goto ExitThisRoutine; /* no access */
577 }
578
579 cp = VTOC(vp);
580 finfop = (struct FndrDirInfo *)&cp->c_attr.ca_finderinfo[0];
581
582 if ( searchBits & SRCHFS_SKIPPACKAGES ) {
583 if ( (SWAP_BE16(finfop->frFlags) & kHasBundle)
584 || (cp->c_desc.cd_nameptr != NULL
585 && is_package_name(cp->c_desc.cd_nameptr, cp->c_desc.cd_namelen)) ) {
586 myResult = 0;
587 goto ExitThisRoutine;
588 }
589 }
590
591 if ( searchBits & SRCHFS_SKIPINAPPROPRIATE ) {
592 if ( cp->c_parentcnid == kRootDirID && cp->c_desc.cd_nameptr != NULL &&
593 is_inappropriate_name(cp->c_desc.cd_nameptr, cp->c_desc.cd_namelen) ) {
594 myResult = 0;
595 goto ExitThisRoutine;
596 }
597 }
598
599 if ( (searchBits & SRCHFS_SKIPINVISIBLE) &&
600 (SWAP_BE16(finfop->frFlags) & kIsInvisible) ) {
601 myResult = 0;
602 goto ExitThisRoutine;
603 }
604
605 myNodeID = cp->c_parentcnid; /* move up the hierarchy */
606 hfs_unlock(VTOC(vp));
607 if (vp->v_type == VDIR) {
608 myErr = vnode_authorize(vp, NULL, (KAUTH_VNODE_SEARCH | KAUTH_VNODE_LIST_DIRECTORY), ctx);
609 } else {
610 myErr = vnode_authorize(vp, NULL, (KAUTH_VNODE_SEARCH), ctx);
611 }
612 vnode_put(vp);
613 vp = NULL;
614 if ( myErr ) {
615 goto ExitThisRoutine; /* no access */
616 }
617 }
618 myResult = 1; /* allow access */
619
620 ExitThisRoutine:
621 if ( vp != NULL ) {
622 hfs_unlock(VTOC(vp));
623 vnode_put(vp);
624 }
625 return ( myResult );
626
627 }
628
629 static int
630 CheckCriteria( ExtendedVCB *vcb,
631 u_long searchBits,
632 struct attrlist *attrList,
633 CatalogRecord *rec,
634 CatalogKey *key,
635 searchinfospec_t *searchInfo1,
636 searchinfospec_t *searchInfo2,
637 Boolean lookForDup )
638 {
639 Boolean matched, atleastone;
640 Boolean isHFSPlus;
641 attrgroup_t searchAttributes;
642 struct cat_attr c_attr;
643 struct cat_fork datafork;
644 struct cat_fork rsrcfork;
645
646 bzero(&c_attr, sizeof(c_attr));
647 isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
648
649 switch (rec->recordType) {
650 case kHFSFolderRecord:
651 case kHFSPlusFolderRecord:
652 if ( (searchBits & SRCHFS_MATCHDIRS) == 0 ) { /* If we are NOT searching folders */
653 matched = false;
654 goto TestDone;
655 }
656 break;
657
658 case kHFSFileRecord:
659 if ( (searchBits & SRCHFS_MATCHFILES) == 0 ) { /* If we are NOT searching files */
660 matched = false;
661 goto TestDone;
662 }
663 break;
664
665 case kHFSPlusFileRecord:
666 /* Check if hardlink links should be skipped. */
667 if (searchBits & SRCHFS_SKIPLINKS) {
668 cnid_t parid = key->hfsPlus.parentID;
669 HFSPlusCatalogFile *filep = (HFSPlusCatalogFile *)rec;
670
671 if ((SWAP_BE32(filep->userInfo.fdType) == kHardLinkFileType) &&
672 (SWAP_BE32(filep->userInfo.fdCreator) == kHFSPlusCreator)) {
673 return (false); /* skip over link records */
674 } else if ((parid == VCBTOHFS(vcb)->hfs_privdir_desc.cd_cnid) &&
675 (filep->bsdInfo.special.linkCount == 0)) {
676 return (false); /* skip over unlinked files */
677 }
678 } else if (key->hfsPlus.parentID == VCBTOHFS(vcb)->hfs_privdir_desc.cd_cnid) {
679 return (false); /* skip over private files */
680 }
681
682 if ( (searchBits & SRCHFS_MATCHFILES) == 0 ) { /* If we are NOT searching files */
683 matched = false;
684 goto TestDone;
685 }
686 break;
687
688 default: /* Never match a thread record or any other type. */
689 return( false ); /* Not a file or folder record, so can't search it */
690 }
691
692 matched = true; /* Assume we got a match */
693 atleastone = false; /* Dont insert unless we match at least one criteria */
694
695 /* First, attempt to match the name -- either partial or complete */
696 if ( attrList->commonattr & ATTR_CMN_NAME ) {
697 if (isHFSPlus) {
698 /* Check for partial/full HFS Plus name match */
699
700 if ( searchBits & SRCHFS_MATCHPARTIALNAMES ) {
701 matched = ComparePartialUnicodeName(key->hfsPlus.nodeName.unicode,
702 key->hfsPlus.nodeName.length,
703 (UniChar*)searchInfo1->name,
704 searchInfo1->nameLength );
705 } else /* full HFS Plus name match */ {
706 matched = (FastUnicodeCompare(key->hfsPlus.nodeName.unicode,
707 key->hfsPlus.nodeName.length,
708 (UniChar*)searchInfo1->name,
709 searchInfo1->nameLength ) == 0);
710 }
711 } else {
712 /* Check for partial/full HFS name match */
713
714 if ( searchBits & SRCHFS_MATCHPARTIALNAMES )
715 matched = ComparePartialPascalName(key->hfs.nodeName, (u_char*)searchInfo1->name);
716 else /* full HFS name match */
717 matched = (FastRelString(key->hfs.nodeName, (u_char*)searchInfo1->name) == 0);
718 }
719
720 #if 1 // Installer workaround (2940423)
721 if ( lookForDup ) {
722 HFSCatalogNodeID parentID;
723 if (isHFSPlus)
724 parentID = key->hfsPlus.parentID;
725 else
726 parentID = key->hfs.parentID;
727
728 if ( matched && parentID == kRootDirID &&
729 IsTargetName( searchInfo1, isHFSPlus ) )
730 matched = false;
731 }
732 #endif // Installer workaround
733
734 if ( matched == false || (searchBits & ~SRCHFS_MATCHPARTIALNAMES) == 0 )
735 goto TestDone; /* no match, or nothing more to compare */
736
737 atleastone = true;
738 }
739
740 /* Convert catalog record into cat_attr format. */
741 cat_convertattr(VCBTOHFS(vcb), rec, &c_attr, &datafork, &rsrcfork);
742
743 if (searchBits & SRCHFS_SKIPINVISIBLE) {
744 int flags;
745
746 switch (rec->recordType) {
747 case kHFSFolderRecord:
748 case kHFSPlusFolderRecord: {
749 struct FndrDirInfo *finder_info;
750
751 finder_info = (struct FndrDirInfo *)&c_attr.ca_finderinfo[0];
752 flags = SWAP_BE16(finder_info->frFlags);
753 break;
754 }
755
756 case kHFSFileRecord:
757 case kHFSPlusFileRecord: {
758 struct FndrFileInfo *finder_info;
759
760 finder_info = (struct FndrFileInfo *)&c_attr.ca_finderinfo[0];
761 flags = SWAP_BE16(finder_info->fdFlags);
762 break;
763 }
764
765 default: {
766 flags = kIsInvisible;
767 break;
768 }
769 }
770
771 if (flags & kIsInvisible) {
772 matched = false;
773 goto TestDone;
774 }
775 }
776
777
778
779 /* Now that we have a record worth searching, see if it matches the search attributes */
780 if (rec->recordType == kHFSFileRecord ||
781 rec->recordType == kHFSPlusFileRecord) {
782 if ((attrList->fileattr & ~ATTR_FILE_VALIDMASK) != 0) { /* attr we do know about */
783 matched = false;
784 goto TestDone;
785 }
786 else if ((attrList->fileattr & ATTR_FILE_VALIDMASK) != 0) {
787 searchAttributes = attrList->fileattr;
788
789 /* File logical length (data fork) */
790 if ( searchAttributes & ATTR_FILE_DATALENGTH ) {
791 matched = CompareWideRange(
792 datafork.cf_size,
793 searchInfo1->f.dataLogicalLength,
794 searchInfo2->f.dataLogicalLength);
795 if (matched == false) goto TestDone;
796 atleastone = true;
797 }
798
799 /* File physical length (data fork) */
800 if ( searchAttributes & ATTR_FILE_DATAALLOCSIZE ) {
801 matched = CompareWideRange(
802 (u_int64_t)datafork.cf_blocks * (u_int64_t)vcb->blockSize,
803 searchInfo1->f.dataPhysicalLength,
804 searchInfo2->f.dataPhysicalLength);
805 if (matched == false) goto TestDone;
806 atleastone = true;
807 }
808
809 /* File logical length (resource fork) */
810 if ( searchAttributes & ATTR_FILE_RSRCLENGTH ) {
811 matched = CompareWideRange(
812 rsrcfork.cf_size,
813 searchInfo1->f.resourceLogicalLength,
814 searchInfo2->f.resourceLogicalLength);
815 if (matched == false) goto TestDone;
816 atleastone = true;
817 }
818
819 /* File physical length (resource fork) */
820 if ( searchAttributes & ATTR_FILE_RSRCALLOCSIZE ) {
821 matched = CompareWideRange(
822 (u_int64_t)rsrcfork.cf_blocks * (u_int64_t)vcb->blockSize,
823 searchInfo1->f.resourcePhysicalLength,
824 searchInfo2->f.resourcePhysicalLength);
825 if (matched == false) goto TestDone;
826 atleastone = true;
827 }
828 }
829 else {
830 atleastone = true; /* to match SRCHFS_MATCHFILES */
831 }
832 }
833 /*
834 * Check the directory attributes
835 */
836 else if (rec->recordType == kHFSFolderRecord ||
837 rec->recordType == kHFSPlusFolderRecord) {
838 if ((attrList->dirattr & ~ATTR_DIR_VALIDMASK) != 0) { /* attr we do know about */
839 matched = false;
840 goto TestDone;
841 }
842 else if ((attrList->dirattr & ATTR_DIR_VALIDMASK) != 0) {
843 searchAttributes = attrList->dirattr;
844
845 /* Directory valence */
846 if ( searchAttributes & ATTR_DIR_ENTRYCOUNT ) {
847 matched = CompareRange(c_attr.ca_entries,
848 searchInfo1->d.numFiles,
849 searchInfo2->d.numFiles );
850 if (matched == false) goto TestDone;
851 atleastone = true;
852 }
853 }
854 else {
855 atleastone = true; /* to match SRCHFS_MATCHDIRS */
856 }
857 }
858
859 /*
860 * Check the common attributes
861 */
862 searchAttributes = attrList->commonattr;
863 if ( (searchAttributes & ATTR_CMN_VALIDMASK) != 0 ) {
864 /* node ID */
865 if ( searchAttributes & ATTR_CMN_OBJID ) {
866 matched = CompareRange(c_attr.ca_fileid,
867 searchInfo1->nodeID,
868 searchInfo2->nodeID );
869 if (matched == false) goto TestDone;
870 atleastone = true;
871 }
872
873 /* Parent ID */
874 if ( searchAttributes & ATTR_CMN_PAROBJID ) {
875 HFSCatalogNodeID parentID;
876
877 if (isHFSPlus)
878 parentID = key->hfsPlus.parentID;
879 else
880 parentID = key->hfs.parentID;
881
882 matched = CompareRange(parentID, searchInfo1->parentDirID,
883 searchInfo2->parentDirID );
884 if (matched == false) goto TestDone;
885 atleastone = true;
886 }
887
888 /* Finder Info & Extended Finder Info where extFinderInfo is last 32 bytes */
889 if ( searchAttributes & ATTR_CMN_FNDRINFO ) {
890 UInt32 *thisValue;
891 thisValue = (UInt32 *) &c_attr.ca_finderinfo;
892
893 /*
894 * Note: ioFlFndrInfo and ioDrUsrWds have the same offset in search info, so
895 * no need to test the object type here.
896 */
897 matched = CompareMasked(thisValue,
898 (UInt32 *)&searchInfo1->finderInfo,
899 (UInt32 *) &searchInfo2->finderInfo, 8);
900 if (matched == false) goto TestDone;
901 atleastone = true;
902 }
903
904 /* Create date */
905 if ( searchAttributes & ATTR_CMN_CRTIME ) {
906 matched = CompareRange(c_attr.ca_itime,
907 searchInfo1->creationDate.tv_sec,
908 searchInfo2->creationDate.tv_sec);
909 if (matched == false) goto TestDone;
910 atleastone = true;
911 }
912
913 /* Mod date */
914 if ( searchAttributes & ATTR_CMN_MODTIME ) {
915 matched = CompareRange(c_attr.ca_mtime,
916 searchInfo1->modificationDate.tv_sec,
917 searchInfo2->modificationDate.tv_sec);
918 if (matched == false) goto TestDone;
919 atleastone = true;
920 }
921
922 /* Change Time */
923 if ( searchAttributes & ATTR_CMN_CHGTIME ) {
924 matched = CompareRange(c_attr.ca_ctime,
925 searchInfo1->changeDate.tv_sec,
926 searchInfo2->changeDate.tv_sec);
927 if (matched == false) goto TestDone;
928 atleastone = true;
929 }
930
931 /* Access date */
932 if ( searchAttributes & ATTR_CMN_ACCTIME ) {
933 matched = CompareRange(c_attr.ca_atime,
934 searchInfo1->accessDate.tv_sec,
935 searchInfo2->accessDate.tv_sec);
936 if (matched == false) goto TestDone;
937 atleastone = true;
938 }
939
940 /* Backup date */
941 if ( searchAttributes & ATTR_CMN_BKUPTIME ) {
942 matched = CompareRange(c_attr.ca_btime,
943 searchInfo1->lastBackupDate.tv_sec,
944 searchInfo2->lastBackupDate.tv_sec);
945 if (matched == false) goto TestDone;
946 atleastone = true;
947 }
948
949 /* User ID */
950 if ( searchAttributes & ATTR_CMN_OWNERID ) {
951 matched = CompareRange(c_attr.ca_uid,
952 searchInfo1->uid, searchInfo2->uid);
953 if (matched == false) goto TestDone;
954 atleastone = true;
955 }
956
957 /* Group ID */
958 if ( searchAttributes & ATTR_CMN_GRPID ) {
959 matched = CompareRange(c_attr.ca_gid,
960 searchInfo1->gid, searchInfo2->gid);
961 if (matched == false) goto TestDone;
962 atleastone = true;
963 }
964
965 /* mode */
966 if ( searchAttributes & ATTR_CMN_ACCESSMASK ) {
967 matched = CompareRange((uint32_t)c_attr.ca_mode,
968 (uint32_t)searchInfo1->mask,
969 (uint32_t)searchInfo2->mask);
970 if (matched == false) goto TestDone;
971 atleastone = true;
972 }
973 }
974
975 /* If we got here w/o matching any, then set to false */
976 if (! atleastone)
977 matched = false;
978
979 TestDone:
980 /*
981 * Finally, determine whether we need to negate the sense of the match
982 * (i.e. find all objects that DON'T match).
983 */
984 if ( searchBits & SRCHFS_NEGATEPARAMS )
985 matched = !matched;
986
987 return( matched );
988 }
989
990
991 /*
992 * Adds another record to the packed array for output
993 */
994 static int
995 InsertMatch(struct hfsmount *hfsmp, uio_t a_uio, CatalogRecord *rec,
996 CatalogKey *key, struct attrlist *returnAttrList,
997 void *attributesBuffer, void *variableBuffer, u_long * nummatches)
998 {
999 int err;
1000 void *rovingAttributesBuffer;
1001 void *rovingVariableBuffer;
1002 u_long packedBufferSize;
1003 u_long privateDir = hfsmp->hfs_privdir_desc.cd_cnid;
1004 struct attrblock attrblk;
1005 struct cat_desc c_desc;
1006 struct cat_attr c_attr;
1007 struct cat_fork datafork;
1008 struct cat_fork rsrcfork;
1009
1010 bzero(&c_desc, sizeof(c_desc));
1011 bzero(&c_attr, sizeof(c_attr));
1012 rovingAttributesBuffer = (char*)attributesBuffer + sizeof(u_long); /* Reserve space for length field */
1013 rovingVariableBuffer = variableBuffer;
1014
1015 /* Convert catalog record into cat_attr format. */
1016 cat_convertattr(hfsmp, rec, &c_attr, &datafork, &rsrcfork);
1017
1018 /* hide our private meta data directory */
1019 if ((privateDir != 0) && (c_attr.ca_fileid == privateDir)) {
1020 err = 0;
1021 goto exit;
1022 }
1023
1024 /* Hide the private journal files */
1025 if (hfsmp->jnl &&
1026 ((c_attr.ca_fileid == hfsmp->hfs_jnlfileid) ||
1027 (c_attr.ca_fileid == hfsmp->hfs_jnlinfoblkid))) {
1028 err = 0;
1029 goto exit;
1030 }
1031
1032 if (returnAttrList->commonattr & ATTR_CMN_NAME) {
1033 cat_convertkey(hfsmp, key, rec, &c_desc);
1034 } else {
1035 c_desc.cd_cnid = c_attr.ca_fileid;
1036 if (hfsmp->hfs_flags & HFS_STANDARD)
1037 c_desc.cd_parentcnid = key->hfs.parentID;
1038 else
1039 c_desc.cd_parentcnid = key->hfsPlus.parentID;
1040 }
1041
1042 attrblk.ab_attrlist = returnAttrList;
1043 attrblk.ab_attrbufpp = &rovingAttributesBuffer;
1044 attrblk.ab_varbufpp = &rovingVariableBuffer;
1045 attrblk.ab_flags = 0;
1046 attrblk.ab_blocksize = 0;
1047
1048 hfs_packattrblk(&attrblk, hfsmp, NULL, &c_desc, &c_attr, &datafork, &rsrcfork, current_proc());
1049
1050 packedBufferSize = (char*)rovingVariableBuffer - (char*)attributesBuffer;
1051
1052 if ( packedBufferSize > uio_resid(a_uio) )
1053 return( errSearchBufferFull );
1054
1055 (* nummatches)++;
1056
1057 *((u_long *)attributesBuffer) = packedBufferSize; /* Store length of fixed + var block */
1058
1059 err = uiomove( (caddr_t)attributesBuffer, packedBufferSize, a_uio ); /* XXX should be packedBufferSize */
1060 exit:
1061 cat_releasedesc(&c_desc);
1062
1063 return( err );
1064 }
1065
1066
1067 static int
1068 UnpackSearchAttributeBlock( struct hfsmount *hfsmp, struct attrlist *alist, searchinfospec_t *searchInfo, void *attributeBuffer )
1069 {
1070 attrgroup_t a;
1071 u_long bufferSize;
1072 boolean_t is_64_bit;
1073
1074 DBG_ASSERT(searchInfo != NULL);
1075
1076 is_64_bit = proc_is64bit(current_proc());
1077
1078 bufferSize = *((uint32_t *)attributeBuffer);
1079 if (bufferSize == 0)
1080 return (EINVAL); /* XXX -DJB is a buffer size of zero ever valid for searchfs? */
1081
1082 ++((uint32_t *)attributeBuffer); /* advance past the size */
1083
1084 /*
1085 * UnPack common attributes
1086 */
1087 a = alist->commonattr;
1088 if ( a != 0 ) {
1089 if ( a & ATTR_CMN_NAME ) {
1090 char *s;
1091 u_int32_t len;
1092
1093 s = (char*) attributeBuffer + ((attrreference_t *) attributeBuffer)->attr_dataoffset;
1094 len = ((attrreference_t *) attributeBuffer)->attr_length;
1095
1096 if (len > sizeof(searchInfo->name))
1097 return (EINVAL);
1098
1099 if (hfsmp->hfs_flags & HFS_STANDARD) {
1100 /* Convert name to pascal string to match HFS B-Tree names */
1101
1102 if (len > 0) {
1103 if (utf8_to_hfs(HFSTOVCB(hfsmp), len-1, s, (u_char*)searchInfo->name) != 0)
1104 return (EINVAL);
1105
1106 searchInfo->nameLength = searchInfo->name[0];
1107 } else {
1108 searchInfo->name[0] = searchInfo->nameLength = 0;
1109 }
1110 ++((attrreference_t *)attributeBuffer);
1111 } else {
1112 size_t ucslen;
1113 /* Convert name to Unicode to match HFS Plus B-Tree names */
1114
1115 if (len > 0) {
1116 if (utf8_decodestr(s, len-1, (UniChar*)searchInfo->name, &ucslen,
1117 sizeof(searchInfo->name), ':', UTF_DECOMPOSED))
1118 return (EINVAL);
1119
1120 searchInfo->nameLength = ucslen / sizeof(UniChar);
1121 } else {
1122 searchInfo->nameLength = 0;
1123 }
1124 ++((attrreference_t *)attributeBuffer);
1125 }
1126 }
1127 if ( a & ATTR_CMN_OBJID ) {
1128 searchInfo->nodeID = ((fsobj_id_t *) attributeBuffer)->fid_objno; /* ignore fid_generation */
1129 ++((fsobj_id_t *)attributeBuffer);
1130 }
1131 if ( a & ATTR_CMN_PAROBJID ) {
1132 searchInfo->parentDirID = ((fsobj_id_t *) attributeBuffer)->fid_objno; /* ignore fid_generation */
1133 ++((fsobj_id_t *)attributeBuffer);
1134 }
1135 if ( a & ATTR_CMN_CRTIME ) {
1136 if (is_64_bit) {
1137 struct user_timespec tmp;
1138 tmp = *((struct user_timespec *)attributeBuffer);
1139 searchInfo->creationDate.tv_sec = (time_t)tmp.tv_sec;
1140 searchInfo->creationDate.tv_nsec = tmp.tv_nsec;
1141 ++((struct user_timespec *)attributeBuffer);
1142 }
1143 else {
1144 searchInfo->creationDate = *((struct timespec *)attributeBuffer);
1145 ++((struct timespec *)attributeBuffer);
1146 }
1147 }
1148 if ( a & ATTR_CMN_MODTIME ) {
1149 if (is_64_bit) {
1150 struct user_timespec tmp;
1151 tmp = *((struct user_timespec *)attributeBuffer);
1152 searchInfo->modificationDate.tv_sec = (time_t)tmp.tv_sec;
1153 searchInfo->modificationDate.tv_nsec = tmp.tv_nsec;
1154 ++((struct user_timespec *)attributeBuffer);
1155 }
1156 else {
1157 searchInfo->modificationDate = *((struct timespec *)attributeBuffer);
1158 ++((struct timespec *)attributeBuffer);
1159 }
1160 }
1161 if ( a & ATTR_CMN_CHGTIME ) {
1162 if (is_64_bit) {
1163 struct user_timespec tmp;
1164 tmp = *((struct user_timespec *)attributeBuffer);
1165 searchInfo->changeDate.tv_sec = (time_t)tmp.tv_sec;
1166 searchInfo->changeDate.tv_nsec = tmp.tv_nsec;
1167 ++((struct user_timespec *)attributeBuffer);
1168 }
1169 else {
1170 searchInfo->changeDate = *((struct timespec *)attributeBuffer);
1171 ++((struct timespec *)attributeBuffer);
1172 }
1173 }
1174 if ( a & ATTR_CMN_ACCTIME ) {
1175 if (is_64_bit) {
1176 struct user_timespec tmp;
1177 tmp = *((struct user_timespec *)attributeBuffer);
1178 searchInfo->accessDate.tv_sec = (time_t)tmp.tv_sec;
1179 searchInfo->accessDate.tv_nsec = tmp.tv_nsec;
1180 ++((struct user_timespec *)attributeBuffer);
1181 }
1182 else {
1183 searchInfo->accessDate = *((struct timespec *)attributeBuffer);
1184 ++((struct timespec *)attributeBuffer);
1185 }
1186 }
1187 if ( a & ATTR_CMN_BKUPTIME ) {
1188 if (is_64_bit) {
1189 struct user_timespec tmp;
1190 tmp = *((struct user_timespec *)attributeBuffer);
1191 searchInfo->lastBackupDate.tv_sec = (time_t)tmp.tv_sec;
1192 searchInfo->lastBackupDate.tv_nsec = tmp.tv_nsec;
1193 ++((struct user_timespec *)attributeBuffer);
1194 }
1195 else {
1196 searchInfo->lastBackupDate = *((struct timespec *)attributeBuffer);
1197 ++((struct timespec *)attributeBuffer);
1198 }
1199 }
1200 if ( a & ATTR_CMN_FNDRINFO ) {
1201 bcopy( attributeBuffer, searchInfo->finderInfo, sizeof(searchInfo->finderInfo) );
1202 (uint8_t *)attributeBuffer += 32;
1203 }
1204 if ( a & ATTR_CMN_OWNERID ) {
1205 searchInfo->uid = *((uid_t *)attributeBuffer);
1206 ++((uid_t *)attributeBuffer);
1207 }
1208 if ( a & ATTR_CMN_GRPID ) {
1209 searchInfo->gid = *((gid_t *)attributeBuffer);
1210 ++((gid_t *)attributeBuffer);
1211 }
1212 if ( a & ATTR_CMN_ACCESSMASK ) {
1213 searchInfo->mask = *((mode_t *)attributeBuffer);
1214 ++((mode_t *)attributeBuffer);
1215 }
1216 }
1217
1218 a = alist->dirattr;
1219 if ( a != 0 ) {
1220 if ( a & ATTR_DIR_ENTRYCOUNT ) {
1221 searchInfo->d.numFiles = *((u_int32_t *)attributeBuffer);
1222 ++((u_int32_t *)attributeBuffer);
1223 }
1224 }
1225
1226 a = alist->fileattr;
1227 if ( a != 0 ) {
1228 if ( a & ATTR_FILE_DATALENGTH ) {
1229 searchInfo->f.dataLogicalLength = *((off_t *)attributeBuffer);
1230 ++((off_t *)attributeBuffer);
1231 }
1232 if ( a & ATTR_FILE_DATAALLOCSIZE ) {
1233 searchInfo->f.dataPhysicalLength = *((off_t *)attributeBuffer);
1234 ++((off_t *)attributeBuffer);
1235 }
1236 if ( a & ATTR_FILE_RSRCLENGTH ) {
1237 searchInfo->f.resourceLogicalLength = *((off_t *)attributeBuffer);
1238 ++((off_t *)attributeBuffer);
1239 }
1240 if ( a & ATTR_FILE_RSRCALLOCSIZE ) {
1241 searchInfo->f.resourcePhysicalLength = *((off_t *)attributeBuffer);
1242 ++((off_t *)attributeBuffer);
1243 }
1244 }
1245
1246 return (0);
1247 }
1248
1249
1250 #if 1 // Installer workaround (2940423)
1251 /* this routine was added as part of the work around where some installers would fail */
1252 /* because they incorrectly assumed search results were in some kind of order. */
1253 /* This routine is used to indentify the problematic target. At this point we */
1254 /* only know of one. This routine could be modified for more (I hope not). */
1255 static Boolean IsTargetName( searchinfospec_t * searchInfoPtr, Boolean isHFSPlus )
1256 {
1257 if ( searchInfoPtr->name == NULL )
1258 return( false );
1259
1260 if (isHFSPlus) {
1261 HFSUniStr255 myName = {
1262 7, /* number of unicode characters */
1263 {
1264 'L','i','b','r','a','r','y'
1265 }
1266 };
1267 if ( FastUnicodeCompare( myName.unicode, myName.length,
1268 (UniChar*)searchInfoPtr->name,
1269 searchInfoPtr->nameLength ) == 0 ) {
1270 return( true );
1271 }
1272
1273 } else {
1274 u_char myName[32] = {
1275 0x07,'L','i','b','r','a','r','y'
1276 };
1277 if ( FastRelString(myName, (u_char*)searchInfoPtr->name) == 0 ) {
1278 return( true );
1279 }
1280 }
1281 return( false );
1282
1283 } /* IsTargetName */
1284 #endif // Installer workaround
1285