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