]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_search.c
84aecbb016dd7c3090fc5a70172f15509f47fe63
[apple/xnu.git] / bsd / hfs / hfs_search.c
1 /*
2 * Copyright (c) 1997-2002 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, 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 int
162 hfs_search( ap )
163 struct vop_searchfs_args *ap; /*
164 struct vnodeop_desc *a_desc;
165 struct vnode *a_vp;
166 void *a_searchparams1;
167 void *a_searchparams2;
168 struct attrlist *a_searchattrs;
169 u_long a_maxmatches;
170 struct timeval *a_timelimit;
171 struct attrlist *a_returnattrs;
172 u_long *a_nummatches;
173 u_long a_scriptcode;
174 u_long a_options;
175 struct uio *a_uio;
176 struct searchstate *a_searchstate;
177 */
178 {
179 ExtendedVCB *vcb = VTOVCB(ap->a_vp);
180 FCB * catalogFCB;
181 searchinfospec_t searchInfo1;
182 searchinfospec_t searchInfo2;
183 void *attributesBuffer;
184 void *variableBuffer;
185 u_long fixedBlockSize;
186 u_long eachReturnBufferSize;
187 struct proc *p = current_proc();
188 int err = E_NONE;
189 int isHFSPlus;
190 int timerExpired = false;
191 int doQuickExit = false;
192 CatalogKey * myCurrentKeyPtr;
193 CatalogRecord * myCurrentDataPtr;
194 CatPosition * myCatPositionPtr;
195 BTScanState myBTScanState;
196 void *user_start = NULL;
197 int user_len;
198
199 /* XXX Parameter check a_searchattrs? */
200
201 *(ap->a_nummatches) = 0;
202
203 if (ap->a_options & ~SRCHFS_VALIDOPTIONSMASK)
204 return (EINVAL);
205
206 if (ap->a_uio->uio_resid <= 0)
207 return (EINVAL);
208
209 isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
210
211 /* UnPack the search boundries, searchInfo1, searchInfo2 */
212 err = UnpackSearchAttributeBlock(ap->a_vp, ap->a_searchattrs,
213 &searchInfo1, ap->a_searchparams1);
214 if (err) return err;
215 err = UnpackSearchAttributeBlock(ap->a_vp, ap->a_searchattrs,
216 &searchInfo2, ap->a_searchparams2);
217 if (err) return err;
218
219 fixedBlockSize = sizeof(u_long) + hfs_attrblksize(ap->a_returnattrs); /* u_long for length longword */
220 eachReturnBufferSize = fixedBlockSize;
221
222 if ( ap->a_returnattrs->commonattr & ATTR_CMN_NAME ) /* XXX should be more robust! */
223 eachReturnBufferSize += kHFSPlusMaxFileNameBytes + 1;
224
225 MALLOC( attributesBuffer, void *, eachReturnBufferSize, M_TEMP, M_WAITOK );
226 variableBuffer = (void*)((char*) attributesBuffer + fixedBlockSize);
227
228 // XXXdbg - have to lock the user's buffer so we don't fault
229 // while holding the shared catalog file lock. see the comment
230 // in hfs_readdir() for more details.
231 //
232 if (VTOHFS(ap->a_vp)->jnl && ap->a_uio->uio_segflg == UIO_USERSPACE) {
233 user_start = ap->a_uio->uio_iov->iov_base;
234 user_len = ap->a_uio->uio_iov->iov_len;
235
236 if ((err = vslock(user_start, user_len)) != 0) {
237 user_start = NULL;
238 goto ExitThisRoutine;
239 }
240 }
241
242 /* Lock catalog b-tree */
243 err = hfs_metafilelocking(VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_SHARED, p);
244 if (err)
245 goto ExitThisRoutine;
246
247 catalogFCB = GetFileControlBlock(vcb->catalogRefNum);
248 myCurrentKeyPtr = NULL;
249 myCurrentDataPtr = NULL;
250 myCatPositionPtr = (CatPosition *)ap->a_searchstate;
251
252 if (ap->a_options & SRCHFS_START) {
253 /* Starting a new search. */
254 /* Make sure the on-disk Catalog file is current */
255 (void) VOP_FSYNC(vcb->catalogRefNum, NOCRED, MNT_WAIT, p);
256 ap->a_options &= ~SRCHFS_START;
257 bzero( (caddr_t)myCatPositionPtr, sizeof( *myCatPositionPtr ) );
258 err = BTScanInitialize(catalogFCB, 0, 0, 0, kCatSearchBufferSize, &myBTScanState);
259
260 #if 1 // Installer workaround (2940423)
261 // hack to get around installer problems when the installer expects search results
262 // to be in key order. At this point the problem appears to be limited to
263 // searches for "Library". The idea here is to go get the "Library" at root
264 // and return it first to the caller then continue the search as normal with
265 // the exception of taking care not to return a duplicate hit (see CheckCriteria)
266 if ( err == E_NONE &&
267 (ap->a_searchattrs->commonattr & ATTR_CMN_NAME) != 0 &&
268 IsTargetName( &searchInfo1, isHFSPlus ) )
269 {
270 CatalogRecord rec;
271 BTreeIterator iterator;
272 FSBufferDescriptor btrec;
273 CatalogKey * keyp;
274 UInt16 reclen;
275 OSErr result;
276
277 bzero( (caddr_t)&iterator, sizeof( iterator ) );
278 keyp = (CatalogKey *) &iterator.key;
279 (void) BuildCatalogKeyUTF8(vcb, kRootDirID, "Library", kUndefinedStrLen, keyp, NULL);
280
281 btrec.bufferAddress = &rec;
282 btrec.itemCount = 1;
283 btrec.itemSize = sizeof( rec );
284
285 result = BTSearchRecord( catalogFCB, &iterator, &btrec, &reclen, &iterator );
286 if ( result == E_NONE ) {
287 if (CheckCriteria(vcb, ap->a_options, ap->a_searchattrs, &rec,
288 keyp, &searchInfo1, &searchInfo2, false) &&
289 CheckAccess(vcb, keyp, ap->a_uio->uio_procp)) {
290
291 result = InsertMatch(ap->a_vp, ap->a_uio, &rec,
292 keyp, ap->a_returnattrs,
293 attributesBuffer, variableBuffer,
294 eachReturnBufferSize, ap->a_nummatches);
295 if (result == E_NONE && *(ap->a_nummatches) >= ap->a_maxmatches)
296 doQuickExit = true;
297 }
298 }
299 }
300 #endif // Installer workaround
301 } else {
302 /* Resuming a search. */
303 err = BTScanInitialize(catalogFCB, myCatPositionPtr->nextNode,
304 myCatPositionPtr->nextRecord,
305 myCatPositionPtr->recordsFound,
306 kCatSearchBufferSize,
307 &myBTScanState);
308 /* Make sure Catalog hasn't changed. */
309 if (err == 0
310 && myCatPositionPtr->writeCount != myBTScanState.btcb->writeCount) {
311 myCatPositionPtr->writeCount = myBTScanState.btcb->writeCount;
312 err = EBUSY; /* catChangedErr */
313 }
314 }
315
316 /* Unlock catalog b-tree */
317 (void) hfs_metafilelocking(VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_RELEASE, p);
318 if (err)
319 goto ExitThisRoutine;
320 #if 1 // Installer workaround (2940423)
321 if ( doQuickExit )
322 goto QuickExit;
323 #endif // Installer workaround
324
325 /*
326 * Check all the catalog btree records...
327 * return the attributes for matching items
328 */
329 for (;;) {
330 struct timeval myCurrentTime;
331 struct timeval myElapsedTime;
332
333 err = BTScanNextRecord(&myBTScanState, timerExpired,
334 (void **)&myCurrentKeyPtr, (void **)&myCurrentDataPtr,
335 NULL);
336 if (err)
337 break;
338
339 /* Resolve any hardlinks */
340 if (isHFSPlus)
341 ResolveHardlink(vcb, (HFSPlusCatalogFile *) myCurrentDataPtr);
342
343 if (CheckCriteria( vcb, ap->a_options, ap->a_searchattrs, myCurrentDataPtr,
344 myCurrentKeyPtr, &searchInfo1, &searchInfo2, true )
345 && CheckAccess(vcb, myCurrentKeyPtr, ap->a_uio->uio_procp)) {
346 err = InsertMatch(ap->a_vp, ap->a_uio, myCurrentDataPtr,
347 myCurrentKeyPtr, ap->a_returnattrs,
348 attributesBuffer, variableBuffer,
349 eachReturnBufferSize, ap->a_nummatches);
350 if (err) {
351 /*
352 * The last match didn't fit so come back
353 * to this record on the next trip.
354 */
355 --myBTScanState.recordsFound;
356 --myBTScanState.recordNum;
357 break;
358 }
359
360 if (*(ap->a_nummatches) >= ap->a_maxmatches)
361 break;
362 }
363
364 /*
365 * Check our elapsed time and bail if we've hit the max.
366 * The idea here is to throttle the amount of time we
367 * spend in the kernel.
368 */
369 myCurrentTime = time;
370 timersub(&myCurrentTime, &myBTScanState.startTime, &myElapsedTime);
371 /* Note: assumes kMaxMicroSecsInKernel is less than 1,000,000 */
372 if (myElapsedTime.tv_sec > 0
373 || myElapsedTime.tv_usec >= kMaxMicroSecsInKernel) {
374 timerExpired = true;
375 }
376 }
377
378 QuickExit:
379 /* Update catalog position */
380 myCatPositionPtr->writeCount = myBTScanState.btcb->writeCount;
381
382 BTScanTerminate(&myBTScanState, &myCatPositionPtr->nextNode,
383 &myCatPositionPtr->nextRecord,
384 &myCatPositionPtr->recordsFound);
385
386 if ( err == E_NONE ) {
387 err = EAGAIN; /* signal to the user to call searchfs again */
388 } else if ( err == errSearchBufferFull ) {
389 if ( *(ap->a_nummatches) > 0 )
390 err = EAGAIN;
391 else
392 err = ENOBUFS;
393 } else if ( err == btNotFound ) {
394 err = E_NONE; /* the entire disk has been searched */
395 } else if ( err == fsBTTimeOutErr ) {
396 err = EAGAIN;
397 }
398
399 ExitThisRoutine:
400 FREE( attributesBuffer, M_TEMP );
401
402 if (VTOHFS(ap->a_vp)->jnl && user_start) {
403 vsunlock(user_start, user_len, TRUE);
404 }
405
406 return (MacToVFSError(err));
407 }
408
409
410 static void
411 ResolveHardlink(ExtendedVCB *vcb, HFSPlusCatalogFile *recp)
412 {
413 if ((recp->recordType == kHFSPlusFileRecord)
414 && (SWAP_BE32(recp->userInfo.fdType) == kHardLinkFileType)
415 && (SWAP_BE32(recp->userInfo.fdCreator) == kHFSPlusCreator)
416 && ((to_bsd_time(recp->createDate) == vcb->vcbCrDate) ||
417 (to_bsd_time(recp->createDate) == VCBTOHFS(vcb)->hfs_metadata_createdate))) {
418 (void) resolvelink(VCBTOHFS(vcb), recp->bsdInfo.special.iNodeNum, recp);
419 }
420 }
421
422
423 static Boolean
424 CompareMasked(const UInt32 *thisValue, const UInt32 *compareData,
425 const UInt32 *compareMask, UInt32 count)
426 {
427 Boolean matched;
428 UInt32 i;
429
430 matched = true; /* Assume it will all match */
431
432 for (i=0; i<count; i++) {
433 if (((*thisValue++ ^ *compareData++) & *compareMask++) != 0) {
434 matched = false;
435 break;
436 }
437 }
438
439 return matched;
440 }
441
442
443 static Boolean
444 ComparePartialUnicodeName (register ConstUniCharArrayPtr str, register ItemCount s_len,
445 register ConstUniCharArrayPtr find, register ItemCount f_len )
446 {
447 if (f_len == 0 || s_len == 0)
448 return FALSE;
449
450 do {
451 if (s_len-- < f_len)
452 return FALSE;
453 } while (FastUnicodeCompare(str++, f_len, find, f_len) != 0);
454
455 return TRUE;
456 }
457
458
459 static Boolean
460 ComparePartialPascalName ( register ConstStr31Param str, register ConstStr31Param find )
461 {
462 register u_char s_len = str[0];
463 register u_char f_len = find[0];
464 register u_char *tsp;
465 Str31 tmpstr;
466
467 if (f_len == 0 || s_len == 0)
468 return FALSE;
469
470 bcopy(str, tmpstr, s_len + 1);
471 tsp = &tmpstr[0];
472
473 while (s_len-- >= f_len) {
474 *tsp = f_len;
475
476 if (FastRelString(tsp++, find) == 0)
477 return TRUE;
478 }
479
480 return FALSE;
481 }
482
483
484 /*
485 * Check to see if caller has access rights to this item
486 */
487
488 static int
489 CheckAccess(ExtendedVCB *theVCBPtr, CatalogKey *theKeyPtr, struct proc *theProcPtr)
490 {
491 Boolean isHFSPlus;
492 int myErr;
493 int myResult;
494 HFSCatalogNodeID myNodeID;
495 unsigned long myPerms;
496 hfsmount_t * my_hfsmountPtr;
497 struct cat_desc my_cat_desc;
498 struct cat_attr my_cat_attr;
499
500 myResult = 0; /* default to "no access" */
501 my_cat_desc.cd_nameptr = NULL;
502 my_cat_desc.cd_namelen = 0;
503
504 if ( theProcPtr->p_ucred->cr_uid == 0 ) {
505 myResult = 1; /* allow access */
506 goto ExitThisRoutine; /* root always has access */
507 }
508
509 my_hfsmountPtr = VCBTOHFS( theVCBPtr );
510 isHFSPlus = ( theVCBPtr->vcbSigWord == kHFSPlusSigWord );
511 if ( isHFSPlus )
512 myNodeID = theKeyPtr->hfsPlus.parentID;
513 else
514 myNodeID = theKeyPtr->hfs.parentID;
515
516 while ( myNodeID >= kRootDirID ) {
517 /* now go get catalog data for this directory */
518 myErr = hfs_metafilelocking( my_hfsmountPtr, kHFSCatalogFileID, LK_SHARED, theProcPtr );
519 if ( myErr )
520 goto ExitThisRoutine; /* no access */
521
522 myErr = cat_idlookup( my_hfsmountPtr, myNodeID, &my_cat_desc, &my_cat_attr, NULL );
523 (void) hfs_metafilelocking( my_hfsmountPtr, kHFSCatalogFileID, LK_RELEASE, theProcPtr );
524 if ( myErr )
525 goto ExitThisRoutine; /* no access */
526
527 myNodeID = my_cat_desc.cd_parentcnid; /* move up the hierarchy */
528 myPerms = DerivePermissionSummary(my_cat_attr.ca_uid, my_cat_attr.ca_gid,
529 my_cat_attr.ca_mode, my_hfsmountPtr->hfs_mp,
530 theProcPtr->p_ucred, theProcPtr );
531 cat_releasedesc( &my_cat_desc );
532
533 if ( (myPerms & X_OK) == 0 )
534 goto ExitThisRoutine; /* no access */
535 }
536
537 myResult = 1; /* allow access */
538
539 ExitThisRoutine:
540 cat_releasedesc( &my_cat_desc );
541 return ( myResult );
542
543 }
544
545 static int
546 CheckCriteria( ExtendedVCB *vcb,
547 u_long searchBits,
548 struct attrlist *attrList,
549 CatalogRecord *rec,
550 CatalogKey *key,
551 searchinfospec_t *searchInfo1,
552 searchinfospec_t *searchInfo2,
553 Boolean lookForDup )
554 {
555 Boolean matched, atleastone;
556 Boolean isHFSPlus;
557 attrgroup_t searchAttributes;
558 struct cat_attr c_attr = {0};
559 struct cat_fork datafork;
560 struct cat_fork rsrcfork;
561
562 isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
563
564 switch (rec->recordType) {
565 case kHFSFolderRecord:
566 case kHFSPlusFolderRecord:
567 if ( (searchBits & SRCHFS_MATCHDIRS) == 0 ) { /* If we are NOT searching folders */
568 matched = false;
569 goto TestDone;
570 }
571 break;
572
573 case kHFSFileRecord:
574 case kHFSPlusFileRecord:
575 if ( (searchBits & SRCHFS_MATCHFILES) == 0 ) { /* If we are NOT searching files */
576 matched = false;
577 goto TestDone;
578 }
579 break;
580
581 default: /* Never match a thread record or any other type. */
582 return( false ); /* Not a file or folder record, so can't search it */
583 }
584
585 matched = true; /* Assume we got a match */
586 atleastone = false; /* Dont insert unless we match at least one criteria */
587
588 /* First, attempt to match the name -- either partial or complete */
589 if ( attrList->commonattr & ATTR_CMN_NAME ) {
590 if (isHFSPlus) {
591 /* Check for partial/full HFS Plus name match */
592
593 if ( searchBits & SRCHFS_MATCHPARTIALNAMES ) {
594 matched = ComparePartialUnicodeName(key->hfsPlus.nodeName.unicode,
595 key->hfsPlus.nodeName.length,
596 (UniChar*)searchInfo1->name,
597 searchInfo1->nameLength );
598 } else /* full HFS Plus name match */ {
599 matched = (FastUnicodeCompare(key->hfsPlus.nodeName.unicode,
600 key->hfsPlus.nodeName.length,
601 (UniChar*)searchInfo1->name,
602 searchInfo1->nameLength ) == 0);
603 }
604 } else {
605 /* Check for partial/full HFS name match */
606
607 if ( searchBits & SRCHFS_MATCHPARTIALNAMES )
608 matched = ComparePartialPascalName(key->hfs.nodeName, (u_char*)searchInfo1->name);
609 else /* full HFS name match */
610 matched = (FastRelString(key->hfs.nodeName, (u_char*)searchInfo1->name) == 0);
611 }
612
613 #if 1 // Installer workaround (2940423)
614 if ( lookForDup ) {
615 HFSCatalogNodeID parentID;
616 if (isHFSPlus)
617 parentID = key->hfsPlus.parentID;
618 else
619 parentID = key->hfs.parentID;
620
621 if ( matched && parentID == kRootDirID &&
622 IsTargetName( searchInfo1, isHFSPlus ) )
623 matched = false;
624 }
625 #endif // Installer workaround
626
627 if ( matched == false || (searchBits & ~SRCHFS_MATCHPARTIALNAMES) == 0 )
628 goto TestDone; /* no match, or nothing more to compare */
629
630 atleastone = true;
631 }
632
633 /* Convert catalog record into cat_attr format. */
634 cat_convertattr(VCBTOHFS(vcb), rec, &c_attr, &datafork, &rsrcfork);
635
636 /* Now that we have a record worth searching, see if it matches the search attributes */
637 if (rec->recordType == kHFSFileRecord ||
638 rec->recordType == kHFSPlusFileRecord) {
639 if ((attrList->fileattr & ~ATTR_FILE_VALIDMASK) != 0) { /* attr we do know about */
640 matched = false;
641 goto TestDone;
642 }
643 else if ((attrList->fileattr & ATTR_FILE_VALIDMASK) != 0) {
644 searchAttributes = attrList->fileattr;
645
646 /* File logical length (data fork) */
647 if ( searchAttributes & ATTR_FILE_DATALENGTH ) {
648 matched = CompareWideRange(
649 datafork.cf_size,
650 searchInfo1->f.dataLogicalLength,
651 searchInfo2->f.dataLogicalLength);
652 if (matched == false) goto TestDone;
653 atleastone = true;
654 }
655
656 /* File physical length (data fork) */
657 if ( searchAttributes & ATTR_FILE_DATAALLOCSIZE ) {
658 matched = CompareWideRange(
659 (u_int64_t)datafork.cf_blocks * (u_int64_t)vcb->blockSize,
660 searchInfo1->f.dataPhysicalLength,
661 searchInfo2->f.dataPhysicalLength);
662 if (matched == false) goto TestDone;
663 atleastone = true;
664 }
665
666 /* File logical length (resource fork) */
667 if ( searchAttributes & ATTR_FILE_RSRCLENGTH ) {
668 matched = CompareWideRange(
669 rsrcfork.cf_size,
670 searchInfo1->f.resourceLogicalLength,
671 searchInfo2->f.resourceLogicalLength);
672 if (matched == false) goto TestDone;
673 atleastone = true;
674 }
675
676 /* File physical length (resource fork) */
677 if ( searchAttributes & ATTR_FILE_RSRCALLOCSIZE ) {
678 matched = CompareWideRange(
679 (u_int64_t)rsrcfork.cf_blocks * (u_int64_t)vcb->blockSize,
680 searchInfo1->f.resourcePhysicalLength,
681 searchInfo2->f.resourcePhysicalLength);
682 if (matched == false) goto TestDone;
683 atleastone = true;
684 }
685 }
686 else {
687 atleastone = true; /* to match SRCHFS_MATCHFILES */
688 }
689 }
690 /*
691 * Check the directory attributes
692 */
693 else if (rec->recordType == kHFSFolderRecord ||
694 rec->recordType == kHFSPlusFolderRecord) {
695 if ((attrList->dirattr & ~ATTR_DIR_VALIDMASK) != 0) { /* attr we do know about */
696 matched = false;
697 goto TestDone;
698 }
699 else if ((attrList->dirattr & ATTR_DIR_VALIDMASK) != 0) {
700 searchAttributes = attrList->dirattr;
701
702 /* Directory valence */
703 if ( searchAttributes & ATTR_DIR_ENTRYCOUNT ) {
704 matched = CompareRange(c_attr.ca_entries,
705 searchInfo1->d.numFiles,
706 searchInfo2->d.numFiles );
707 if (matched == false) goto TestDone;
708 atleastone = true;
709 }
710 }
711 else {
712 atleastone = true; /* to match SRCHFS_MATCHDIRS */
713 }
714 }
715
716 /*
717 * Check the common attributes
718 */
719 searchAttributes = attrList->commonattr;
720 if ( (searchAttributes & ATTR_CMN_VALIDMASK) != 0 ) {
721 /* node ID */
722 if ( searchAttributes & ATTR_CMN_OBJID ) {
723 matched = CompareRange(c_attr.ca_fileid,
724 searchInfo1->nodeID,
725 searchInfo2->nodeID );
726 if (matched == false) goto TestDone;
727 atleastone = true;
728 }
729
730 /* Parent ID */
731 if ( searchAttributes & ATTR_CMN_PAROBJID ) {
732 HFSCatalogNodeID parentID;
733
734 if (isHFSPlus)
735 parentID = key->hfsPlus.parentID;
736 else
737 parentID = key->hfs.parentID;
738
739 matched = CompareRange(parentID, searchInfo1->parentDirID,
740 searchInfo2->parentDirID );
741 if (matched == false) goto TestDone;
742 atleastone = true;
743 }
744
745 /* Finder Info & Extended Finder Info where extFinderInfo is last 32 bytes */
746 if ( searchAttributes & ATTR_CMN_FNDRINFO ) {
747 UInt32 *thisValue;
748 thisValue = (UInt32 *) &c_attr.ca_finderinfo;
749
750 /*
751 * Note: ioFlFndrInfo and ioDrUsrWds have the same offset in search info, so
752 * no need to test the object type here.
753 */
754 matched = CompareMasked(thisValue,
755 (UInt32 *)&searchInfo1->finderInfo,
756 (UInt32 *) &searchInfo2->finderInfo, 8);
757 if (matched == false) goto TestDone;
758 atleastone = true;
759 }
760
761 /* Create date */
762 if ( searchAttributes & ATTR_CMN_CRTIME ) {
763 matched = CompareRange(c_attr.ca_itime,
764 searchInfo1->creationDate.tv_sec,
765 searchInfo2->creationDate.tv_sec);
766 if (matched == false) goto TestDone;
767 atleastone = true;
768 }
769
770 /* Mod date */
771 if ( searchAttributes & ATTR_CMN_MODTIME ) {
772 matched = CompareRange(c_attr.ca_mtime,
773 searchInfo1->modificationDate.tv_sec,
774 searchInfo2->modificationDate.tv_sec);
775 if (matched == false) goto TestDone;
776 atleastone = true;
777 }
778
779 /* Change Time */
780 if ( searchAttributes & ATTR_CMN_CHGTIME ) {
781 matched = CompareRange(c_attr.ca_ctime,
782 searchInfo1->changeDate.tv_sec,
783 searchInfo2->changeDate.tv_sec);
784 if (matched == false) goto TestDone;
785 atleastone = true;
786 }
787
788 /* Access date */
789 if ( searchAttributes & ATTR_CMN_ACCTIME ) {
790 matched = CompareRange(c_attr.ca_atime,
791 searchInfo1->accessDate.tv_sec,
792 searchInfo2->accessDate.tv_sec);
793 if (matched == false) goto TestDone;
794 atleastone = true;
795 }
796
797 /* Backup date */
798 if ( searchAttributes & ATTR_CMN_BKUPTIME ) {
799 matched = CompareRange(c_attr.ca_btime,
800 searchInfo1->lastBackupDate.tv_sec,
801 searchInfo2->lastBackupDate.tv_sec);
802 if (matched == false) goto TestDone;
803 atleastone = true;
804 }
805
806 /* User ID */
807 if ( searchAttributes & ATTR_CMN_OWNERID ) {
808 matched = CompareRange(c_attr.ca_uid,
809 searchInfo1->uid, searchInfo2->uid);
810 if (matched == false) goto TestDone;
811 atleastone = true;
812 }
813
814 /* Group ID */
815 if ( searchAttributes & ATTR_CMN_GRPID ) {
816 matched = CompareRange(c_attr.ca_gid,
817 searchInfo1->gid, searchInfo2->gid);
818 if (matched == false) goto TestDone;
819 atleastone = true;
820 }
821
822 /* mode */
823 if ( searchAttributes & ATTR_CMN_ACCESSMASK ) {
824 matched = CompareRange((u_long)c_attr.ca_mode,
825 (u_long)searchInfo1->mask,
826 (u_long)searchInfo2->mask);
827 if (matched == false) goto TestDone;
828 atleastone = true;
829 }
830 }
831
832 /* If we got here w/o matching any, then set to false */
833 if (! atleastone)
834 matched = false;
835
836 TestDone:
837 /*
838 * Finally, determine whether we need to negate the sense of the match
839 * (i.e. find all objects that DON'T match).
840 */
841 if ( searchBits & SRCHFS_NEGATEPARAMS )
842 matched = !matched;
843
844 return( matched );
845 }
846
847
848 /*
849 * Adds another record to the packed array for output
850 */
851 static int
852 InsertMatch( struct vnode *root_vp, struct uio *a_uio, CatalogRecord *rec,
853 CatalogKey *key, struct attrlist *returnAttrList, void *attributesBuffer,
854 void *variableBuffer, u_long bufferSize, u_long * nummatches )
855 {
856 int err;
857 void *rovingAttributesBuffer;
858 void *rovingVariableBuffer;
859 u_long packedBufferSize;
860 ExtendedVCB *vcb = VTOVCB(root_vp);
861 Boolean isHFSPlus = vcb->vcbSigWord == kHFSPlusSigWord;
862 u_long privateDir = VTOHFS(root_vp)->hfs_private_metadata_dir;
863 struct attrblock attrblk;
864 struct cat_desc c_desc = {0};
865 struct cat_attr c_attr = {0};
866 struct cat_fork datafork;
867 struct cat_fork rsrcfork;
868
869 rovingAttributesBuffer = (char*)attributesBuffer + sizeof(u_long); /* Reserve space for length field */
870 rovingVariableBuffer = variableBuffer;
871
872 /* Convert catalog record into cat_attr format. */
873 cat_convertattr(VTOHFS(root_vp), rec, &c_attr, &datafork, &rsrcfork);
874
875 /* hide our private meta data directory */
876 if ((privateDir != 0) && (c_attr.ca_fileid == privateDir)) {
877 err = 0;
878 goto exit;
879 }
880
881 /* Hide the private journal files */
882 if (VTOHFS(root_vp)->jnl &&
883 ((c_attr.ca_fileid == VTOHFS(root_vp)->hfs_jnlfileid) ||
884 (c_attr.ca_fileid == VTOHFS(root_vp)->hfs_jnlinfoblkid))) {
885 err = 0;
886 goto exit;
887 }
888
889 if (returnAttrList->commonattr & ATTR_CMN_NAME) {
890 cat_convertkey(VTOHFS(root_vp), key, rec, &c_desc);
891 } else {
892 c_desc.cd_cnid = c_attr.ca_fileid;
893 if (isHFSPlus)
894 c_desc.cd_parentcnid = key->hfsPlus.parentID;
895 else
896 c_desc.cd_parentcnid = key->hfs.parentID;
897 }
898
899 /* hide open files that have been deleted */
900 if ((privateDir != 0) && (c_desc.cd_parentcnid == privateDir)) {
901 err = 0;
902 goto exit;
903 }
904
905 attrblk.ab_attrlist = returnAttrList;
906 attrblk.ab_attrbufpp = &rovingAttributesBuffer;
907 attrblk.ab_varbufpp = &rovingVariableBuffer;
908 attrblk.ab_flags = 0;
909 attrblk.ab_blocksize = 0;
910
911 hfs_packattrblk(&attrblk, VTOHFS(root_vp), NULL, &c_desc, &c_attr, &datafork, &rsrcfork);
912
913 packedBufferSize = (char*)rovingVariableBuffer - (char*)attributesBuffer;
914
915 if ( packedBufferSize > a_uio->uio_resid )
916 return( errSearchBufferFull );
917
918 (* nummatches)++;
919
920 *((u_long *)attributesBuffer) = packedBufferSize; /* Store length of fixed + var block */
921
922 err = uiomove( (caddr_t)attributesBuffer, packedBufferSize, a_uio ); /* XXX should be packedBufferSize */
923 exit:
924 cat_releasedesc(&c_desc);
925
926 return( err );
927 }
928
929
930 static int
931 UnpackSearchAttributeBlock( struct vnode *vp, struct attrlist *alist, searchinfospec_t *searchInfo, void *attributeBuffer )
932 {
933 attrgroup_t a;
934 u_long bufferSize;
935
936 DBG_ASSERT(searchInfo != NULL);
937
938 bufferSize = *((u_long *)attributeBuffer);
939 if (bufferSize == 0)
940 return (EINVAL); /* XXX -DJB is a buffer size of zero ever valid for searchfs? */
941
942 ++((u_long *)attributeBuffer); /* advance past the size */
943
944 /*
945 * UnPack common attributes
946 */
947 a = alist->commonattr;
948 if ( a != 0 ) {
949 if ( a & ATTR_CMN_NAME ) {
950 char *s = (char*) attributeBuffer + ((attrreference_t *) attributeBuffer)->attr_dataoffset;
951 size_t len = ((attrreference_t *) attributeBuffer)->attr_length;
952
953 if (len > sizeof(searchInfo->name))
954 return (EINVAL);
955
956 if (VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) {
957 size_t ucslen;
958 /* Convert name to Unicode to match HFS Plus B-Tree names */
959
960 if (len > 0) {
961 if (utf8_decodestr(s, len-1, (UniChar*)searchInfo->name, &ucslen,
962 sizeof(searchInfo->name), ':', UTF_DECOMPOSED))
963 return (EINVAL);
964
965 searchInfo->nameLength = ucslen / sizeof(UniChar);
966 } else {
967 searchInfo->nameLength = 0;
968 }
969 ++((attrreference_t *)attributeBuffer);
970
971 } else {
972 /* Convert name to pascal string to match HFS B-Tree names */
973
974 if (len > 0) {
975 if (utf8_to_hfs(VTOVCB(vp), len-1, s, (u_char*)searchInfo->name) != 0)
976 return (EINVAL);
977
978 searchInfo->nameLength = searchInfo->name[0];
979 } else {
980 searchInfo->name[0] = searchInfo->nameLength = 0;
981 }
982 ++((attrreference_t *)attributeBuffer);
983 }
984 }
985 if ( a & ATTR_CMN_OBJID ) {
986 searchInfo->nodeID = ((fsobj_id_t *) attributeBuffer)->fid_objno; /* ignore fid_generation */
987 ++((fsobj_id_t *)attributeBuffer);
988 }
989 if ( a & ATTR_CMN_PAROBJID ) {
990 searchInfo->parentDirID = ((fsobj_id_t *) attributeBuffer)->fid_objno; /* ignore fid_generation */
991 ++((fsobj_id_t *)attributeBuffer);
992 }
993 if ( a & ATTR_CMN_CRTIME ) {
994 searchInfo->creationDate = *((struct timespec *)attributeBuffer);
995 ++((struct timespec *)attributeBuffer);
996 }
997 if ( a & ATTR_CMN_MODTIME ) {
998 searchInfo->modificationDate = *((struct timespec *)attributeBuffer);
999 ++((struct timespec *)attributeBuffer);
1000 }
1001 if ( a & ATTR_CMN_CHGTIME ) {
1002 searchInfo->changeDate = *((struct timespec *)attributeBuffer);
1003 ++((struct timespec *)attributeBuffer);
1004 }
1005 if ( a & ATTR_CMN_ACCTIME ) {
1006 searchInfo->accessDate = *((struct timespec *)attributeBuffer);
1007 ++((struct timespec *)attributeBuffer);
1008 }
1009 if ( a & ATTR_CMN_BKUPTIME ) {
1010 searchInfo->lastBackupDate = *((struct timespec *)attributeBuffer);
1011 ++((struct timespec *)attributeBuffer);
1012 }
1013 if ( a & ATTR_CMN_FNDRINFO ) {
1014 bcopy( attributeBuffer, searchInfo->finderInfo, sizeof(u_long) * 8 );
1015 (u_long *)attributeBuffer += 8;
1016 }
1017 if ( a & ATTR_CMN_BKUPTIME ) {
1018 searchInfo->lastBackupDate = *((struct timespec *)attributeBuffer);
1019 ++((struct timespec *)attributeBuffer);
1020 }
1021 if ( a & ATTR_CMN_OWNERID ) {
1022 searchInfo->uid = *((uid_t *)attributeBuffer);
1023 ++((uid_t *)attributeBuffer);
1024 }
1025 if ( a & ATTR_CMN_GRPID ) {
1026 searchInfo->gid = *((gid_t *)attributeBuffer);
1027 ++((gid_t *)attributeBuffer);
1028 }
1029 if ( a & ATTR_CMN_ACCESSMASK ) {
1030 searchInfo->mask = *((mode_t *)attributeBuffer);
1031 ++((mode_t *)attributeBuffer);
1032 }
1033 }
1034
1035 a = alist->dirattr;
1036 if ( a != 0 ) {
1037 if ( a & ATTR_DIR_ENTRYCOUNT ) {
1038 searchInfo->d.numFiles = *((u_long *)attributeBuffer);
1039 ++((u_long *)attributeBuffer);
1040 }
1041 }
1042
1043 a = alist->fileattr;
1044 if ( a != 0 ) {
1045 if ( a & ATTR_FILE_DATALENGTH ) {
1046 searchInfo->f.dataLogicalLength = *((off_t *)attributeBuffer);
1047 ++((off_t *)attributeBuffer);
1048 }
1049 if ( a & ATTR_FILE_DATAALLOCSIZE ) {
1050 searchInfo->f.dataPhysicalLength = *((off_t *)attributeBuffer);
1051 ++((off_t *)attributeBuffer);
1052 }
1053 if ( a & ATTR_FILE_RSRCLENGTH ) {
1054 searchInfo->f.resourceLogicalLength = *((off_t *)attributeBuffer);
1055 ++((off_t *)attributeBuffer);
1056 }
1057 if ( a & ATTR_FILE_RSRCALLOCSIZE ) {
1058 searchInfo->f.resourcePhysicalLength = *((off_t *)attributeBuffer);
1059 ++((off_t *)attributeBuffer);
1060 }
1061 }
1062
1063 return (0);
1064 }
1065
1066
1067 #if 1 // Installer workaround (2940423)
1068 /* this routine was added as part of the work around where some installers would fail */
1069 /* because they incorrectly assumed search results were in some kind of order. */
1070 /* This routine is used to indentify the problematic target. At this point we */
1071 /* only know of one. This routine could be modified for more (I hope not). */
1072 static Boolean IsTargetName( searchinfospec_t * searchInfoPtr, Boolean isHFSPlus )
1073 {
1074 if ( searchInfoPtr->name == NULL )
1075 return( false );
1076
1077 if (isHFSPlus) {
1078 HFSUniStr255 myName = {
1079 7, /* number of unicode characters */
1080 {
1081 'L','i','b','r','a','r','y'
1082 }
1083 };
1084 if ( FastUnicodeCompare( myName.unicode, myName.length,
1085 (UniChar*)searchInfoPtr->name,
1086 searchInfoPtr->nameLength ) == 0 ) {
1087 return( true );
1088 }
1089
1090 } else {
1091 u_char myName[32] = {
1092 0x07,'L','i','b','r','a','r','y'
1093 };
1094 if ( FastRelString(myName, (u_char*)searchInfoPtr->name) == 0 ) {
1095 return( true );
1096 }
1097 }
1098 return( false );
1099
1100 } /* IsTargetName */
1101 #endif // Installer workaround
1102