]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_search.c
xnu-344.12.2.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_search.c
CommitLineData
1c79356b 1/*
9bccf70c 2 * Copyright (c) 1997-2002 Apple Computer, Inc. All rights reserved.
1c79356b
A
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@
1c79356b 21 *
9bccf70c 22 * @(#)hfs_search.c
1c79356b
A
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"
9bccf70c
A
41#include "hfs_catalog.h"
42#include "hfs_attrlist.h"
43#include "hfs_endian.h"
44
1c79356b
A
45#include "hfscommon/headers/FileMgrInternal.h"
46#include "hfscommon/headers/CatalogPrivate.h"
47#include "hfscommon/headers/HFSUnicodeWrappers.h"
14353aa8
A
48#include "hfscommon/headers/BTreesPrivate.h"
49#include "hfscommon/headers/BTreeScanner.h"
1c79356b
A
50
51
9bccf70c
A
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;
1c79356b 86
9bccf70c 87static void ResolveHardlink(ExtendedVCB *vcb, HFSPlusCatalogFile *recp);
1c79356b 88
1c79356b 89
9bccf70c
A
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,
1c79356b
A
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))
9bccf70c
A
122
123#if 1 // Installer workaround (2940423)
d52fe63f 124static Boolean IsTargetName( searchinfospec_t * searchInfoPtr, Boolean isHFSPlus );
9bccf70c
A
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);
1c79356b 139
9bccf70c
A
140extern int resolvelink(struct hfsmount *hfsmp, u_long linkref,
141 struct HFSPlusCatalogFile *recp);
1c79356b
A
142
143/************************************************************************/
9bccf70c 144/* Entry for searchfs() */
1c79356b
A
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 )
9bccf70c
A
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 */
1c79356b 178{
9bccf70c
A
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;
b4c24cb9
A
196 void *user_start = NULL;
197 int user_len;
1c79356b
A
198
199 /* XXX Parameter check a_searchattrs? */
200
201 *(ap->a_nummatches) = 0;
202
9bccf70c
A
203 if (ap->a_options & ~SRCHFS_VALIDOPTIONSMASK)
204 return (EINVAL);
1c79356b
A
205
206 if (ap->a_uio->uio_resid <= 0)
207 return (EINVAL);
208
209 isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
1c79356b
A
210
211 /* UnPack the search boundries, searchInfo1, searchInfo2 */
9bccf70c
A
212 err = UnpackSearchAttributeBlock(ap->a_vp, ap->a_searchattrs,
213 &searchInfo1, ap->a_searchparams1);
1c79356b 214 if (err) return err;
9bccf70c
A
215 err = UnpackSearchAttributeBlock(ap->a_vp, ap->a_searchattrs,
216 &searchInfo2, ap->a_searchparams2);
1c79356b
A
217 if (err) return err;
218
9bccf70c 219 fixedBlockSize = sizeof(u_long) + hfs_attrblksize(ap->a_returnattrs); /* u_long for length longword */
1c79356b 220 eachReturnBufferSize = fixedBlockSize;
9bccf70c 221
1c79356b
A
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
b4c24cb9
A
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
1c79356b 242 /* Lock catalog b-tree */
9bccf70c
A
243 err = hfs_metafilelocking(VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_SHARED, p);
244 if (err)
1c79356b 245 goto ExitThisRoutine;
1c79356b 246
9bccf70c 247 catalogFCB = GetFileControlBlock(vcb->catalogRefNum);
14353aa8
A
248 myCurrentKeyPtr = NULL;
249 myCurrentDataPtr = NULL;
250 myCatPositionPtr = (CatPosition *)ap->a_searchstate;
1c79356b 251
14353aa8
A
252 if (ap->a_options & SRCHFS_START) {
253 /* Starting a new search. */
9bccf70c
A
254 /* Make sure the on-disk Catalog file is current */
255 (void) VOP_FSYNC(vcb->catalogRefNum, NOCRED, MNT_WAIT, p);
14353aa8
A
256 ap->a_options &= ~SRCHFS_START;
257 bzero( (caddr_t)myCatPositionPtr, sizeof( *myCatPositionPtr ) );
258 err = BTScanInitialize(catalogFCB, 0, 0, 0, kCatSearchBufferSize, &myBTScanState);
9bccf70c
A
259
260#if 1 // Installer workaround (2940423)
d52fe63f
A
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
9bccf70c 285 result = BTSearchRecord( catalogFCB, &iterator, &btrec, &reclen, &iterator );
d52fe63f 286 if ( result == E_NONE ) {
9bccf70c 287 if (CheckCriteria(vcb, ap->a_options, ap->a_searchattrs, &rec,
d52fe63f 288 keyp, &searchInfo1, &searchInfo2, false) &&
9bccf70c 289 CheckAccess(vcb, keyp, ap->a_uio->uio_procp)) {
d52fe63f 290
9bccf70c 291 result = InsertMatch(ap->a_vp, ap->a_uio, &rec,
d52fe63f
A
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
14353aa8
A
301 } else {
302 /* Resuming a search. */
303 err = BTScanInitialize(catalogFCB, myCatPositionPtr->nextNode,
304 myCatPositionPtr->nextRecord,
305 myCatPositionPtr->recordsFound,
306 kCatSearchBufferSize,
307 &myBTScanState);
14353aa8
A
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 */
1c79356b 313 }
14353aa8 314 }
1c79356b 315
14353aa8
A
316 /* Unlock catalog b-tree */
317 (void) hfs_metafilelocking(VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_RELEASE, p);
318 if (err)
319 goto ExitThisRoutine;
9bccf70c 320#if 1 // Installer workaround (2940423)
d52fe63f
A
321 if ( doQuickExit )
322 goto QuickExit;
9bccf70c
A
323#endif // Installer workaround
324
14353aa8
A
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;
1c79356b 332
14353aa8
A
333 err = BTScanNextRecord(&myBTScanState, timerExpired,
334 (void **)&myCurrentKeyPtr, (void **)&myCurrentDataPtr,
335 NULL);
336 if (err)
337 break;
338
9bccf70c
A
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);
14353aa8
A
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;
1c79356b 357 break;
14353aa8 358 }
9bccf70c 359
14353aa8
A
360 if (*(ap->a_nummatches) >= ap->a_maxmatches)
361 break;
362 }
1c79356b 363
14353aa8
A
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;
1c79356b
A
375 }
376 }
9bccf70c 377
d52fe63f 378QuickExit:
14353aa8
A
379 /* Update catalog position */
380 myCatPositionPtr->writeCount = myBTScanState.btcb->writeCount;
1c79356b 381
9bccf70c
A
382 BTScanTerminate(&myBTScanState, &myCatPositionPtr->nextNode,
383 &myCatPositionPtr->nextRecord,
384 &myCatPositionPtr->recordsFound);
1c79356b
A
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 */
14353aa8
A
395 } else if ( err == fsBTTimeOutErr ) {
396 err = EAGAIN;
1c79356b
A
397 }
398
399ExitThisRoutine:
400 FREE( attributesBuffer, M_TEMP );
401
b4c24cb9
A
402 if (VTOHFS(ap->a_vp)->jnl && user_start) {
403 vsunlock(user_start, user_len, TRUE);
404 }
405
14353aa8 406 return (MacToVFSError(err));
1c79356b
A
407}
408
409
9bccf70c
A
410static void
411ResolveHardlink(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
1c79356b
A
423static Boolean
424CompareMasked(const UInt32 *thisValue, const UInt32 *compareData,
9bccf70c 425 const UInt32 *compareMask, UInt32 count)
1c79356b
A
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
443static Boolean
444ComparePartialUnicodeName (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
459static Boolean
460ComparePartialPascalName ( 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 */
9bccf70c 487
1c79356b 488static int
9bccf70c 489CheckAccess(ExtendedVCB *theVCBPtr, CatalogKey *theKeyPtr, struct proc *theProcPtr)
1c79356b 490{
9bccf70c
A
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
539ExitThisRoutine:
540 cat_releasedesc( &my_cat_desc );
541 return ( myResult );
542
1c79356b
A
543}
544
9bccf70c
A
545static int
546CheckCriteria( ExtendedVCB *vcb,
547 u_long searchBits,
548 struct attrlist *attrList,
549 CatalogRecord *rec,
d52fe63f 550 CatalogKey *key,
9bccf70c 551 searchinfospec_t *searchInfo1,
d52fe63f
A
552 searchinfospec_t *searchInfo2,
553 Boolean lookForDup )
1c79356b
A
554{
555 Boolean matched, atleastone;
556 Boolean isHFSPlus;
557 attrgroup_t searchAttributes;
9bccf70c
A
558 struct cat_attr c_attr = {0};
559 struct cat_fork datafork;
560 struct cat_fork rsrcfork;
1c79356b
A
561
562 isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
563
9bccf70c
A
564 switch (rec->recordType) {
565 case kHFSFolderRecord:
566 case kHFSPlusFolderRecord:
1c79356b
A
567 if ( (searchBits & SRCHFS_MATCHDIRS) == 0 ) { /* If we are NOT searching folders */
568 matched = false;
569 goto TestDone;
570 }
571 break;
572
9bccf70c
A
573 case kHFSFileRecord:
574 case kHFSPlusFileRecord:
1c79356b
A
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 }
d52fe63f 612
9bccf70c 613#if 1 // Installer workaround (2940423)
d52fe63f
A
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
9bccf70c 626
1c79356b
A
627 if ( matched == false || (searchBits & ~SRCHFS_MATCHPARTIALNAMES) == 0 )
628 goto TestDone; /* no match, or nothing more to compare */
629
630 atleastone = true;
631 }
9bccf70c
A
632
633 /* Convert catalog record into cat_attr format. */
634 cat_convertattr(VCBTOHFS(vcb), rec, &c_attr, &datafork, &rsrcfork);
1c79356b
A
635
636 /* Now that we have a record worth searching, see if it matches the search attributes */
9bccf70c
A
637 if (rec->recordType == kHFSFileRecord ||
638 rec->recordType == kHFSPlusFileRecord) {
14353aa8 639 if ((attrList->fileattr & ~ATTR_FILE_VALIDMASK) != 0) { /* attr we do know about */
1c79356b
A
640 matched = false;
641 goto TestDone;
642 }
14353aa8 643 else if ((attrList->fileattr & ATTR_FILE_VALIDMASK) != 0) {
1c79356b
A
644 searchAttributes = attrList->fileattr;
645
646 /* File logical length (data fork) */
647 if ( searchAttributes & ATTR_FILE_DATALENGTH ) {
648 matched = CompareWideRange(
9bccf70c 649 datafork.cf_size,
1c79356b
A
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(
9bccf70c 659 (u_int64_t)datafork.cf_blocks * (u_int64_t)vcb->blockSize,
1c79356b
A
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(
9bccf70c 669 rsrcfork.cf_size,
1c79356b
A
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(
9bccf70c 679 (u_int64_t)rsrcfork.cf_blocks * (u_int64_t)vcb->blockSize,
1c79356b
A
680 searchInfo1->f.resourcePhysicalLength,
681 searchInfo2->f.resourcePhysicalLength);
682 if (matched == false) goto TestDone;
683 atleastone = true;
684 }
685 }
686 else {
9bccf70c 687 atleastone = true; /* to match SRCHFS_MATCHFILES */
1c79356b
A
688 }
689 }
690 /*
691 * Check the directory attributes
692 */
9bccf70c
A
693 else if (rec->recordType == kHFSFolderRecord ||
694 rec->recordType == kHFSPlusFolderRecord) {
1c79356b
A
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 ) {
9bccf70c
A
704 matched = CompareRange(c_attr.ca_entries,
705 searchInfo1->d.numFiles,
706 searchInfo2->d.numFiles );
1c79356b
A
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 ) {
1c79356b
A
721 /* node ID */
722 if ( searchAttributes & ATTR_CMN_OBJID ) {
9bccf70c
A
723 matched = CompareRange(c_attr.ca_fileid,
724 searchInfo1->nodeID,
725 searchInfo2->nodeID );
1c79356b
A
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
9bccf70c
A
739 matched = CompareRange(parentID, searchInfo1->parentDirID,
740 searchInfo2->parentDirID );
1c79356b
A
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;
9bccf70c 748 thisValue = (UInt32 *) &c_attr.ca_finderinfo;
1c79356b
A
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 */
9bccf70c
A
754 matched = CompareMasked(thisValue,
755 (UInt32 *)&searchInfo1->finderInfo,
756 (UInt32 *) &searchInfo2->finderInfo, 8);
1c79356b
A
757 if (matched == false) goto TestDone;
758 atleastone = true;
759 }
760
761 /* Create date */
762 if ( searchAttributes & ATTR_CMN_CRTIME ) {
9bccf70c
A
763 matched = CompareRange(c_attr.ca_itime,
764 searchInfo1->creationDate.tv_sec,
765 searchInfo2->creationDate.tv_sec);
1c79356b
A
766 if (matched == false) goto TestDone;
767 atleastone = true;
768 }
769
770 /* Mod date */
771 if ( searchAttributes & ATTR_CMN_MODTIME ) {
9bccf70c
A
772 matched = CompareRange(c_attr.ca_mtime,
773 searchInfo1->modificationDate.tv_sec,
774 searchInfo2->modificationDate.tv_sec);
1c79356b
A
775 if (matched == false) goto TestDone;
776 atleastone = true;
777 }
778
779 /* Change Time */
780 if ( searchAttributes & ATTR_CMN_CHGTIME ) {
9bccf70c
A
781 matched = CompareRange(c_attr.ca_ctime,
782 searchInfo1->changeDate.tv_sec,
783 searchInfo2->changeDate.tv_sec);
1c79356b
A
784 if (matched == false) goto TestDone;
785 atleastone = true;
786 }
787
9bccf70c
A
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
1c79356b
A
797 /* Backup date */
798 if ( searchAttributes & ATTR_CMN_BKUPTIME ) {
9bccf70c
A
799 matched = CompareRange(c_attr.ca_btime,
800 searchInfo1->lastBackupDate.tv_sec,
801 searchInfo2->lastBackupDate.tv_sec);
1c79356b
A
802 if (matched == false) goto TestDone;
803 atleastone = true;
804 }
805
806 /* User ID */
807 if ( searchAttributes & ATTR_CMN_OWNERID ) {
9bccf70c
A
808 matched = CompareRange(c_attr.ca_uid,
809 searchInfo1->uid, searchInfo2->uid);
1c79356b
A
810 if (matched == false) goto TestDone;
811 atleastone = true;
812 }
813
814 /* Group ID */
815 if ( searchAttributes & ATTR_CMN_GRPID ) {
9bccf70c
A
816 matched = CompareRange(c_attr.ca_gid,
817 searchInfo1->gid, searchInfo2->gid);
1c79356b
A
818 if (matched == false) goto TestDone;
819 atleastone = true;
820 }
821
822 /* mode */
823 if ( searchAttributes & ATTR_CMN_ACCESSMASK ) {
9bccf70c
A
824 matched = CompareRange((u_long)c_attr.ca_mode,
825 (u_long)searchInfo1->mask,
826 (u_long)searchInfo2->mask);
1c79356b
A
827 if (matched == false) goto TestDone;
828 atleastone = true;
829 }
1c79356b
A
830 }
831
832 /* If we got here w/o matching any, then set to false */
833 if (! atleastone)
834 matched = false;
835
836TestDone:
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 */
851static int
9bccf70c 852InsertMatch( struct vnode *root_vp, struct uio *a_uio, CatalogRecord *rec,
1c79356b
A
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;
1c79356b
A
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;
9bccf70c
A
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;
1c79356b
A
868
869 rovingAttributesBuffer = (char*)attributesBuffer + sizeof(u_long); /* Reserve space for length field */
870 rovingVariableBuffer = variableBuffer;
1c79356b 871
9bccf70c
A
872 /* Convert catalog record into cat_attr format. */
873 cat_convertattr(VTOHFS(root_vp), rec, &c_attr, &datafork, &rsrcfork);
1c79356b 874
9bccf70c
A
875 /* hide our private meta data directory */
876 if ((privateDir != 0) && (c_attr.ca_fileid == privateDir)) {
877 err = 0;
878 goto exit;
879 }
1c79356b 880
b4c24cb9
A
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
9bccf70c
A
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 }
1c79356b 898
9bccf70c
A
899 /* hide open files that have been deleted */
900 if ((privateDir != 0) && (c_desc.cd_parentcnid == privateDir)) {
901 err = 0;
902 goto exit;
1c79356b
A
903 }
904
9bccf70c
A
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;
1c79356b 910
9bccf70c 911 hfs_packattrblk(&attrblk, VTOHFS(root_vp), NULL, &c_desc, &c_attr, &datafork, &rsrcfork);
1c79356b
A
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 */
9bccf70c
A
923exit:
924 cat_releasedesc(&c_desc);
1c79356b
A
925
926 return( err );
927}
928
929
930static int
931UnpackSearchAttributeBlock( 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 }
9bccf70c
A
1005 if ( a & ATTR_CMN_ACCTIME ) {
1006 searchInfo->accessDate = *((struct timespec *)attributeBuffer);
1007 ++((struct timespec *)attributeBuffer);
1008 }
1c79356b
A
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
9bccf70c 1067#if 1 // Installer workaround (2940423)
d52fe63f
A
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). */
1072static 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 */
9bccf70c 1101#endif // Installer workaround
d52fe63f 1102