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