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