1/*
2 * Copyright (c) 1997-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 *
28 *	@(#)hfs_search.c
29 */
30/*
31 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
32 * support for mandatory and extensible security protections.  This notice
33 * is included in support of clause 2.2 (b) of the Apple Public License,
34 * Version 2.0.
35 */
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/kernel.h>
40#include <sys/file.h>
41#include <sys/proc.h>
42#include <sys/conf.h>
43#include <mach/machine/vm_types.h>
44#include <sys/vnode.h>
45#include <sys/malloc.h>
46#include <sys/signalvar.h>
47#include <sys/attr.h>
48#include <sys/utfconv.h>
49#include <sys/kauth.h>
50#include <sys/vnode_internal.h>
51
52#if CONFIG_MACF
53#include <security/mac_framework.h>
54#endif
55
56#include "hfs.h"
57#include "hfs_dbg.h"
58#include "hfs_catalog.h"
59#include "hfs_attrlist.h"
60#include "hfs_endian.h"
61
62#include "hfscommon/headers/FileMgrInternal.h"
63#include "hfscommon/headers/HFSUnicodeWrappers.h"
64#include "hfscommon/headers/BTreesPrivate.h"
65#include "hfscommon/headers/BTreeScanner.h"
66#include "hfscommon/headers/CatalogPrivate.h"
67
68/* Search criterea. */
69struct directoryInfoSpec
70{
71	u_int32_t   numFiles;
72};
73
74struct fileInfoSpec
75{
76	off_t		dataLogicalLength;
77	off_t		dataPhysicalLength;
78	off_t		resourceLogicalLength;
79	off_t		resourcePhysicalLength;
80};
81
82struct searchinfospec
83{
84	u_char			name[kHFSPlusMaxFileNameBytes];
85	u_long			nameLength;
86	char			attributes;		// see IM:Files 2-100
87	u_long			nodeID;
88	u_long			parentDirID;
89	struct timespec		creationDate;
90	struct timespec		modificationDate;
91	struct timespec		changeDate;
92	struct timespec		accessDate;
93	struct timespec		lastBackupDate;
94	u_int8_t		finderInfo[32];
95	uid_t			uid;
96	gid_t			gid;
97	mode_t			mask;
98	struct fileInfoSpec	f;
99	struct directoryInfoSpec d;
100};
101typedef struct searchinfospec searchinfospec_t;
102
103static void ResolveHardlink(struct hfsmount *hfsmp, HFSPlusCatalogFile *recp);
104
105
106static int UnpackSearchAttributeBlock(struct hfsmount *hfsmp, struct attrlist *alist,
107		searchinfospec_t *searchInfo, void *attributeBuffer);
108
109static int CheckCriteria(	ExtendedVCB *vcb,
110							u_long searchBits,
111							struct attrlist *attrList,
112							CatalogRecord *rec,
113							CatalogKey *key,
114							searchinfospec_t *searchInfo1,
115							searchinfospec_t *searchInfo2 );
116
117static int CheckAccess(ExtendedVCB *vcb, u_long searchBits, CatalogKey *key, struct vfs_context *ctx);
118
119static int InsertMatch(struct hfsmount *hfsmp, uio_t a_uio, CatalogRecord *rec,
120			CatalogKey *key, struct attrlist *returnAttrList,
121			void *attributesBuffer, void *variableBuffer,
122			u_long * nummatches );
123
124static Boolean CompareRange(u_long val, u_long low, u_long high);
125static Boolean CompareWideRange(u_int64_t val, u_int64_t low, u_int64_t high);
126
127static Boolean CompareRange( u_long val, u_long low, u_long high )
128{
129	return( (val >= low) && (val <= high) );
130}
131
132static Boolean CompareWideRange( u_int64_t val, u_int64_t low, u_int64_t high )
133{
134	return( (val >= low) && (val <= high) );
135}
136//#define CompareRange(val, low, high)	((val >= low) && (val <= high))
137
138
139/************************************************************************/
140/* Entry for searchfs()                                                 */
141/************************************************************************/
142
143#define	errSearchBufferFull	101	/* Internal search errors */
144/*
145#
146#% searchfs	vp	L L L
147#
148vnop_searchfs {
149    IN struct vnode *vp;
150    IN off_t length;
151    IN int flags;
152    IN kauth_cred_t cred;
153    IN struct proc *p;
154};
155*/
156
157__private_extern__
158int
159hfs_vnop_search(ap)
160	struct vnop_searchfs_args *ap; /*
161		struct vnodeop_desc *a_desc;
162		struct vnode *a_vp;
163		void *a_searchparams1;
164		void *a_searchparams2;
165		struct attrlist *a_searchattrs;
166		u_long a_maxmatches;
167		struct timeval *a_timelimit;
168		struct attrlist *a_returnattrs;
169		u_long *a_nummatches;
170		u_long a_scriptcode;
171		u_long a_options;
172		struct uio *a_uio;
173		struct searchstate *a_searchstate;
174		vfs_context_t a_context;
175	*/
176{
177	ExtendedVCB *vcb = VTOVCB(ap->a_vp);
178	struct hfsmount *hfsmp;
179	FCB * catalogFCB;
180	searchinfospec_t searchInfo1;
181	searchinfospec_t searchInfo2;
182	void *attributesBuffer;
183	void *variableBuffer;
184	u_long fixedBlockSize;
185	u_long eachReturnBufferSize;
186	struct proc *p = current_proc();
187	int err = E_NONE;
188	int isHFSPlus;
189	int timerExpired = false;
190	CatalogKey * myCurrentKeyPtr;
191	CatalogRecord * myCurrentDataPtr;
192	CatPosition * myCatPositionPtr;
193	BTScanState myBTScanState;
194	user_addr_t user_start = 0;
195	user_size_t user_len = 0;
196	int32_t searchTime;
197	int lockflags;
198
199	/* XXX Parameter check a_searchattrs? */
200
201	*(ap->a_nummatches) = 0;
202
203	if (ap->a_options & ~SRCHFS_VALIDOPTIONSMASK)
204		return (EINVAL);
205
206	/*
207	 * Reject requests for unsupported attributes.
208	 */
209	if ((ap->a_returnattrs->commonattr & ~HFS_ATTR_CMN_VALID) ||
210	    (ap->a_returnattrs->volattr != 0) ||
211	    (ap->a_returnattrs->dirattr & ~HFS_ATTR_DIR_VALID) ||
212	    (ap->a_returnattrs->fileattr & ~HFS_ATTR_FILE_VALID) ||
213	    (ap->a_returnattrs->forkattr != 0)) {
214		return (EINVAL);
215	}
216
217	/* SRCHFS_SKIPLINKS requires root access.
218	 * This option cannot be used with either
219	 * the ATTR_CMN_NAME or ATTR_CMN_PAROBJID
220	 * attributes.
221	 */
222	if (ap->a_options & SRCHFS_SKIPLINKS) {
223		attrgroup_t attrs;
224
225		attrs = ap->a_searchattrs->commonattr | ap->a_returnattrs->commonattr;
226		if (attrs & (ATTR_CMN_NAME | ATTR_CMN_PAROBJID))
227			return (EINVAL);
228		if ((err = vfs_context_suser(ap->a_context)))
229			return (err);
230	}
231
232	// If both 32-bit and 64-bit parent ids or file ids are given
233	// then return an error.
234
235	attrgroup_t test_attrs=ap->a_searchattrs->commonattr;
236
237	if (((test_attrs & ATTR_CMN_OBJID) && (test_attrs & ATTR_CMN_FILEID)) ||
238			((test_attrs & ATTR_CMN_PARENTID) && (test_attrs & ATTR_CMN_PAROBJID)))
239		return (EINVAL);
240
241
242	if (uio_resid(ap->a_uio) <= 0)
243		return (EINVAL);
244
245	isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
246	hfsmp = VTOHFS(ap->a_vp);
247
248	searchTime = kMaxMicroSecsInKernel;
249	if (ap->a_timelimit->tv_sec == 0 &&
250	    ap->a_timelimit->tv_usec > 0 &&
251	    ap->a_timelimit->tv_usec < kMaxMicroSecsInKernel) {
252		searchTime = ap->a_timelimit->tv_usec;
253	}
254
255	/* UnPack the search boundries, searchInfo1, searchInfo2 */
256	err = UnpackSearchAttributeBlock(hfsmp, ap->a_searchattrs,
257				&searchInfo1, ap->a_searchparams1);
258	if (err) return err;
259	err = UnpackSearchAttributeBlock(hfsmp, ap->a_searchattrs,
260				&searchInfo2, ap->a_searchparams2);
261	if (err) return err;
262
263	//shadow search bits if 64-bit file/parent ids are used
264	if (ap->a_searchattrs->commonattr & ATTR_CMN_FILEID)
265		ap->a_searchattrs->commonattr |= ATTR_CMN_OBJID;
266	if (ap->a_searchattrs->commonattr & ATTR_CMN_PARENTID)
267		ap->a_searchattrs->commonattr |= ATTR_CMN_PAROBJID;
268
269	fixedBlockSize = sizeof(u_int32_t) + hfs_attrblksize(ap->a_returnattrs);	/* u_int32_t for length word */
270
271	eachReturnBufferSize = fixedBlockSize;
272
273	if ( ap->a_returnattrs->commonattr & ATTR_CMN_NAME )	/* XXX should be more robust! */
274		eachReturnBufferSize += kHFSPlusMaxFileNameBytes + 1;
275
276	MALLOC( attributesBuffer, void *, eachReturnBufferSize, M_TEMP, M_WAITOK );
277	variableBuffer = (void*)((char*) attributesBuffer + fixedBlockSize);
278
279	// XXXdbg - have to lock the user's buffer so we don't fault
280	// while holding the shared catalog file lock.  see the comment
281	// in hfs_readdir() for more details.
282	//
283	if (hfsmp->jnl && uio_isuserspace(ap->a_uio)) {
284		user_start = uio_curriovbase(ap->a_uio);
285		user_len = uio_curriovlen(ap->a_uio);
286
287		if ((err = vslock(user_start, user_len)) != 0) {
288			user_start = 0;
289			goto ExitThisRoutine;
290		}
291	}
292
293	lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
294
295	catalogFCB = GetFileControlBlock(vcb->catalogRefNum);
296	myCurrentKeyPtr = NULL;
297	myCurrentDataPtr = NULL;
298	myCatPositionPtr = (CatPosition *)ap->a_searchstate;
299
300	if (ap->a_options & SRCHFS_START) {
301		/* Starting a new search. */
302		/* Make sure the on-disk Catalog file is current */
303		(void) hfs_fsync(vcb->catalogRefNum, MNT_WAIT, 0, p);
304		if (hfsmp->jnl) {
305		    hfs_systemfile_unlock(hfsmp, lockflags);
306		    journal_flush(hfsmp->jnl);
307		    lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
308		}
309
310		ap->a_options &= ~SRCHFS_START;
311		bzero((caddr_t)myCatPositionPtr, sizeof(*myCatPositionPtr));
312		err = BTScanInitialize(catalogFCB, 0, 0, 0, kCatSearchBufferSize, &myBTScanState);
313		if (err) {
314			goto ExitThisRoutine;
315		}
316	} else {
317		/* Resuming a search. */
318		err = BTScanInitialize(catalogFCB, myCatPositionPtr->nextNode,
319					myCatPositionPtr->nextRecord,
320					myCatPositionPtr->recordsFound,
321					kCatSearchBufferSize,
322					&myBTScanState);
323		/* Make sure Catalog hasn't changed. */
324		if (err == 0
325		&&  myCatPositionPtr->writeCount != myBTScanState.btcb->writeCount) {
326			myCatPositionPtr->writeCount = myBTScanState.btcb->writeCount;
327			err = EBUSY; /* catChangedErr */
328		}
329	}
330	hfs_systemfile_unlock(hfsmp, lockflags);
331
332	if (err)
333		goto ExitThisRoutine;
334
335	/*
336	 * Check all the catalog btree records...
337	 *   return the attributes for matching items
338	 */
339	for (;;) {
340		struct timeval myCurrentTime;
341		struct timeval myElapsedTime;
342
343		err = BTScanNextRecord(&myBTScanState, timerExpired,
344			(void **)&myCurrentKeyPtr, (void **)&myCurrentDataPtr,
345			NULL);
346		if (err)
347			break;
348
349		/* Resolve any hardlinks */
350		if (isHFSPlus && (ap->a_options & SRCHFS_SKIPLINKS) == 0) {
351			ResolveHardlink(vcb, (HFSPlusCatalogFile *)myCurrentDataPtr);
352		}
353		if (CheckCriteria( vcb, ap->a_options, ap->a_searchattrs, myCurrentDataPtr,
354				myCurrentKeyPtr, &searchInfo1, &searchInfo2 )
355		&&  CheckAccess(vcb, ap->a_options, myCurrentKeyPtr, ap->a_context)) {
356			err = InsertMatch(hfsmp, ap->a_uio, myCurrentDataPtr,
357					myCurrentKeyPtr, ap->a_returnattrs,
358					attributesBuffer, variableBuffer, ap->a_nummatches);
359			if (err) {
360				/*
361				 * The last match didn't fit so come back
362				 * to this record on the next trip.
363				 */
364				--myBTScanState.recordsFound;
365				--myBTScanState.recordNum;
366				break;
367			}
368
369			if (*(ap->a_nummatches) >= ap->a_maxmatches)
370				break;
371		}
372
373		/*
374		 * Check our elapsed time and bail if we've hit the max.
375		 * The idea here is to throttle the amount of time we
376		 * spend in the kernel.
377		 */
378		microuptime(&myCurrentTime);
379		timersub(&myCurrentTime, &myBTScanState.startTime, &myElapsedTime);
380		/* Note: assumes kMaxMicroSecsInKernel is less than 1,000,000 */
381		if (myElapsedTime.tv_sec > 0
382		||  myElapsedTime.tv_usec >= searchTime) {
383			timerExpired = true;
384		}
385	}
386
387	/* Update catalog position */
388	myCatPositionPtr->writeCount = myBTScanState.btcb->writeCount;
389
390	BTScanTerminate(&myBTScanState, &myCatPositionPtr->nextNode,
391			&myCatPositionPtr->nextRecord,
392			&myCatPositionPtr->recordsFound);
393
394	if ( err == E_NONE ) {
395		err = EAGAIN;	/* signal to the user to call searchfs again */
396	} else if ( err == errSearchBufferFull ) {
397		if ( *(ap->a_nummatches) > 0 )
398			err = EAGAIN;
399 		else
400			err = ENOBUFS;
401	} else if ( err == btNotFound ) {
402		err = E_NONE;	/* the entire disk has been searched */
403	} else if ( err == fsBTTimeOutErr ) {
404		err = EAGAIN;
405	}
406
407ExitThisRoutine:
408    FREE( attributesBuffer, M_TEMP );
409
410	if (hfsmp->jnl && user_start) {
411		vsunlock(user_start, user_len, TRUE);
412	}
413
414	return (MacToVFSError(err));
415}
416
417
418static void
419ResolveHardlink(struct hfsmount *hfsmp, HFSPlusCatalogFile *recp)
420{
421	u_int32_t type, creator;
422	int isdirlink = 0;
423	int isfilelink = 0;
424	time_t filecreatedate;
425
426	if (recp->recordType != kHFSPlusFileRecord) {
427		return;
428	}
429	type = SWAP_BE32(recp->userInfo.fdType);
430	creator = SWAP_BE32(recp->userInfo.fdCreator);
431	filecreatedate = to_bsd_time(recp->createDate);
432
433	if ((type == kHardLinkFileType && creator == kHFSPlusCreator) &&
434	    (filecreatedate == (time_t)hfsmp->vcbCrDate ||
435	     filecreatedate == (time_t)hfsmp->hfs_metadata_createdate)) {
436		isfilelink = 1;
437	} else if ((type == kHFSAliasType && creator == kHFSAliasCreator) &&
438	           (recp->flags & kHFSHasLinkChainMask) &&
439	           (filecreatedate == (time_t)hfsmp->vcbCrDate ||
440	            filecreatedate == (time_t)hfsmp->hfs_metadata_createdate)) {
441		isdirlink = 1;
442	}
443
444	if (isfilelink || isdirlink) {
445		cnid_t saved_cnid;
446		int lockflags;
447
448		/* Export link's cnid (a unique value) instead of inode's cnid */
449		saved_cnid = recp->fileID;
450		lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
451
452		(void) cat_resolvelink(hfsmp, recp->hl_linkReference, isdirlink, recp);
453
454		recp->fileID = saved_cnid;
455		hfs_systemfile_unlock(hfsmp, lockflags);
456	}
457}
458
459
460static Boolean
461CompareMasked(const u_int32_t *thisValue, const u_int32_t *compareData,
462		const u_int32_t *compareMask, u_int32_t count)
463{
464	Boolean	matched;
465	u_int32_t	i;
466
467	matched = true;		/* Assume it will all match */
468
469	for (i=0; i<count; i++) {
470		if (((*thisValue++ ^ *compareData++) & *compareMask++) != 0) {
471			matched = false;
472			break;
473		}
474	}
475
476	return matched;
477}
478
479
480static Boolean
481ComparePartialUnicodeName (register ConstUniCharArrayPtr str, register ItemCount s_len,
482			   register ConstUniCharArrayPtr find, register ItemCount f_len )
483{
484	if (f_len == 0 || s_len == 0)
485		return FALSE;
486
487	do {
488		if (s_len-- < f_len)
489			return FALSE;
490	} while (FastUnicodeCompare(str++, f_len, find, f_len) != 0);
491
492	return TRUE;
493}
494
495
496static Boolean
497ComparePartialPascalName ( register ConstStr31Param str, register ConstStr31Param find )
498{
499	register u_char s_len = str[0];
500	register u_char f_len = find[0];
501	register u_char *tsp;
502	Str31 tmpstr;
503
504	if (f_len == 0 || s_len == 0)
505		return FALSE;
506
507	bcopy(str, tmpstr, s_len + 1);
508	tsp = &tmpstr[0];
509
510	while (s_len-- >= f_len) {
511		*tsp = f_len;
512
513		if (FastRelString(tsp++, find) == 0)
514			return TRUE;
515	}
516
517	return FALSE;
518}
519
520
521//
522// Determine if a name is "inappropriate" where the definition
523// of "inappropriate" is up to higher level execs.  Currently
524// that's limited to /System.
525//
526static int
527is_inappropriate_name(const char *name, int len)
528{
529    const char *bad_names[] = { "System" };
530    int   bad_len[]   = {        6 };
531    int  i;
532
533    for(i=0; i < (int) (sizeof(bad_names) / sizeof(bad_names[0])); i++) {
534	if (len == bad_len[i] && strncmp(name, bad_names[i], strlen(bad_names[i]) + 1) == 0) {
535	    return 1;
536	}
537    }
538
539    // if we get here, no name matched
540    return 0;
541}
542
543
544
545/*
546 * Check to see if caller has access rights to this item
547 */
548
549static int
550CheckAccess(ExtendedVCB *theVCBPtr, u_long searchBits, CatalogKey *theKeyPtr, struct vfs_context *ctx)
551{
552	Boolean				isHFSPlus;
553	int					myErr;
554	int					myResult;
555	HFSCatalogNodeID 	myNodeID;
556	hfsmount_t *		hfsmp;
557	struct FndrDirInfo	*finfop;
558	struct vnode * 		vp = NULL;
559
560	myResult = 0;	/* default to "no access" */
561
562	if (!vfs_context_suser(ctx))  {
563		myResult = 1;	/* allow access */
564		goto ExitThisRoutine; /* root always has access */
565	}
566
567	hfsmp = VCBTOHFS( theVCBPtr );
568	isHFSPlus = ( theVCBPtr->vcbSigWord == kHFSPlusSigWord );
569	if ( isHFSPlus )
570		myNodeID = theKeyPtr->hfsPlus.parentID;
571	else
572		myNodeID = theKeyPtr->hfs.parentID;
573
574	while ( myNodeID >= kRootDirID ) {
575		cnode_t *	cp;
576
577		/* now go get catalog data for this directory */
578		myErr = hfs_vget(hfsmp, myNodeID, &vp, 0);
579		if ( myErr ) {
580			goto ExitThisRoutine;	/* no access */
581		}
582
583		cp = VTOC(vp);
584		finfop = (struct FndrDirInfo *)&cp->c_attr.ca_finderinfo[0];
585
586		if ( searchBits & SRCHFS_SKIPPACKAGES ) {
587		    if (   (SWAP_BE16(finfop->frFlags) & kHasBundle)
588			|| (cp->c_desc.cd_nameptr != NULL
589			    && is_package_name((const char *)cp->c_desc.cd_nameptr, cp->c_desc.cd_namelen)) ) {
590				myResult = 0;
591				goto ExitThisRoutine;
592		    }
593		}
594
595		if ( searchBits & SRCHFS_SKIPINAPPROPRIATE ) {
596		    if ( cp->c_parentcnid == kRootDirID && cp->c_desc.cd_nameptr != NULL &&
597			     is_inappropriate_name((const char *)cp->c_desc.cd_nameptr, cp->c_desc.cd_namelen) ) {
598				myResult = 0;
599				goto ExitThisRoutine;
600		    }
601		}
602
603		if ( (searchBits & SRCHFS_SKIPINVISIBLE) &&
604			 (SWAP_BE16(finfop->frFlags) & kIsInvisible) ) {
605		    myResult = 0;
606		    goto ExitThisRoutine;
607		}
608
609		myNodeID = cp->c_parentcnid;	/* move up the hierarchy */
610		hfs_unlock(VTOC(vp));
611
612#if CONFIG_MACF
613		if (vp->v_type == VDIR) {
614			myErr = mac_vnode_check_readdir(ctx, vp);
615		} else {
616			myErr = mac_vnode_check_stat(ctx, NOCRED, vp);
617		}
618		if (myErr) {
619			vnode_put(vp);
620			vp = NULL;
621			goto ExitThisRoutine;
622		}
623#endif /* MAC */
624
625		if (vp->v_type == VDIR) {
626		    myErr = vnode_authorize(vp, NULL, (KAUTH_VNODE_SEARCH | KAUTH_VNODE_LIST_DIRECTORY), ctx);
627		} else {
628		    myErr = vnode_authorize(vp, NULL, (KAUTH_VNODE_SEARCH), ctx);
629		}
630		vnode_put(vp);
631		vp = NULL;
632		if ( myErr ) {
633			goto ExitThisRoutine;	/* no access */
634		}
635	}
636	myResult = 1;	/* allow access */
637
638ExitThisRoutine:
639	if ( vp != NULL ) {
640		hfs_unlock(VTOC(vp));
641		vnode_put(vp);
642	}
643	return ( myResult );
644
645}
646
647static int
648CheckCriteria(	ExtendedVCB *vcb,
649				u_long searchBits,
650				struct attrlist *attrList,
651				CatalogRecord *rec,
652				CatalogKey *key,
653				searchinfospec_t  *searchInfo1,
654				searchinfospec_t *searchInfo2 )
655{
656	Boolean matched, atleastone;
657	Boolean isHFSPlus;
658	attrgroup_t searchAttributes;
659	struct cat_attr c_attr;
660	struct cat_fork datafork;
661	struct cat_fork rsrcfork;
662
663	bzero(&c_attr, sizeof(c_attr));
664	isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
665
666	switch (rec->recordType) {
667	case kHFSFolderRecord:
668	case kHFSPlusFolderRecord:
669		if ( (searchBits & SRCHFS_MATCHDIRS) == 0 ) {	/* If we are NOT searching folders */
670			matched = false;
671			goto TestDone;
672		}
673		break;
674
675	case kHFSFileRecord:
676		if ( (searchBits & SRCHFS_MATCHFILES) == 0 ) {	/* If we are NOT searching files */
677			matched = false;
678			goto TestDone;
679		}
680		break;
681
682	case kHFSPlusFileRecord:
683		/* Check if hardlink links should be skipped. */
684		if (searchBits & SRCHFS_SKIPLINKS) {
685			cnid_t parid = key->hfsPlus.parentID;
686			HFSPlusCatalogFile *filep = (HFSPlusCatalogFile *)rec;
687
688			if ((SWAP_BE32(filep->userInfo.fdType) == kHardLinkFileType) &&
689			    (SWAP_BE32(filep->userInfo.fdCreator) == kHFSPlusCreator)) {
690				return (false);	/* skip over file link records */
691			} else if ((parid == vcb->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
692			           (filep->bsdInfo.special.linkCount == 0)) {
693				return (false);	/* skip over unlinked files */
694			} else if ((SWAP_BE32(filep->userInfo.fdType) == kHFSAliasType) &&
695			           (SWAP_BE32(filep->userInfo.fdCreator) == kHFSAliasCreator) &&
696			           (filep->flags & kHFSHasLinkChainMask)) {
697				return (false);	/* skip over dir link records */
698			}
699		} else if (key->hfsPlus.parentID == vcb->hfs_private_desc[FILE_HARDLINKS].cd_cnid) {
700			return (false);	/* skip over private files */
701		} else if (key->hfsPlus.parentID == vcb->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
702			return (false);	/* skip over private files */
703		}
704
705		if ( (searchBits & SRCHFS_MATCHFILES) == 0 ) {	/* If we are NOT searching files */
706			matched = false;
707			goto TestDone;
708		}
709		break;
710
711	default:	/* Never match a thread record or any other type. */
712		return( false );	/* Not a file or folder record, so can't search it */
713	}
714
715	matched = true;		/* Assume we got a match */
716	atleastone = false;	/* Dont insert unless we match at least one criteria */
717
718	/* First, attempt to match the name -- either partial or complete */
719	if ( attrList->commonattr & ATTR_CMN_NAME ) {
720		if (isHFSPlus) {
721			/* Check for partial/full HFS Plus name match */
722
723			if ( searchBits & SRCHFS_MATCHPARTIALNAMES ) {
724				matched = ComparePartialUnicodeName(key->hfsPlus.nodeName.unicode,
725								    key->hfsPlus.nodeName.length,
726								    (UniChar*)searchInfo1->name,
727								    searchInfo1->nameLength );
728			} else /* full HFS Plus name match */ {
729				matched = (FastUnicodeCompare(key->hfsPlus.nodeName.unicode,
730							      key->hfsPlus.nodeName.length,
731							      (UniChar*)searchInfo1->name,
732							      searchInfo1->nameLength ) == 0);
733			}
734		} else {
735			/* Check for partial/full HFS name match */
736
737			if ( searchBits & SRCHFS_MATCHPARTIALNAMES )
738				matched = ComparePartialPascalName(key->hfs.nodeName, (u_char*)searchInfo1->name);
739			else /* full HFS name match */
740				matched = (FastRelString(key->hfs.nodeName, (u_char*)searchInfo1->name) == 0);
741		}
742
743		if ( matched == false || (searchBits & ~SRCHFS_MATCHPARTIALNAMES) == 0 )
744			goto TestDone;	/* no match, or nothing more to compare */
745
746		atleastone = true;
747	}
748
749	/* Convert catalog record into cat_attr format. */
750	cat_convertattr(VCBTOHFS(vcb), rec, &c_attr, &datafork, &rsrcfork);
751
752	if (searchBits & SRCHFS_SKIPINVISIBLE) {
753	    int flags;
754
755	    switch (rec->recordType) {
756		case kHFSFolderRecord:
757		case kHFSPlusFolderRecord: {
758		    struct FndrDirInfo *finder_info;
759
760		    finder_info = (struct FndrDirInfo *)&c_attr.ca_finderinfo[0];
761		    flags = SWAP_BE16(finder_info->frFlags);
762		    break;
763		}
764
765		case kHFSFileRecord:
766		case kHFSPlusFileRecord: {
767		    struct FndrFileInfo *finder_info;
768
769		    finder_info = (struct FndrFileInfo *)&c_attr.ca_finderinfo[0];
770		    flags = SWAP_BE16(finder_info->fdFlags);
771		    break;
772		}
773
774		default: {
775		    flags = kIsInvisible;
776		    break;
777		}
778	    }
779
780	    if (flags & kIsInvisible) {
781		matched = false;
782		goto TestDone;
783	    }
784	}
785
786
787
788	/* Now that we have a record worth searching, see if it matches the search attributes */
789	if (rec->recordType == kHFSFileRecord ||
790	    rec->recordType == kHFSPlusFileRecord) {
791		if ((attrList->fileattr & ~ATTR_FILE_VALIDMASK) != 0) {	/* attr we do know about  */
792			matched = false;
793			goto TestDone;
794		}
795		else if ((attrList->fileattr & ATTR_FILE_VALIDMASK) != 0) {
796		searchAttributes = attrList->fileattr;
797
798		/* File logical length (data fork) */
799		if ( searchAttributes & ATTR_FILE_DATALENGTH ) {
800			matched = CompareWideRange(
801			    datafork.cf_size,
802			    searchInfo1->f.dataLogicalLength,
803			    searchInfo2->f.dataLogicalLength);
804			if (matched == false) goto TestDone;
805				atleastone = true;
806		}
807
808		/* File physical length (data fork) */
809		if ( searchAttributes & ATTR_FILE_DATAALLOCSIZE ) {
810			matched = CompareWideRange(
811			    (u_int64_t)datafork.cf_blocks * (u_int64_t)vcb->blockSize,
812			    searchInfo1->f.dataPhysicalLength,
813			    searchInfo2->f.dataPhysicalLength);
814			if (matched == false) goto TestDone;
815				atleastone = true;
816		}
817
818		/* File logical length (resource fork) */
819		if ( searchAttributes & ATTR_FILE_RSRCLENGTH ) {
820			matched = CompareWideRange(
821			    rsrcfork.cf_size,
822			    searchInfo1->f.resourceLogicalLength,
823			    searchInfo2->f.resourceLogicalLength);
824			if (matched == false) goto TestDone;
825				atleastone = true;
826		}
827
828		/* File physical length (resource fork) */
829		if ( searchAttributes & ATTR_FILE_RSRCALLOCSIZE ) {
830			matched = CompareWideRange(
831			    (u_int64_t)rsrcfork.cf_blocks * (u_int64_t)vcb->blockSize,
832			    searchInfo1->f.resourcePhysicalLength,
833			    searchInfo2->f.resourcePhysicalLength);
834			if (matched == false) goto TestDone;
835				atleastone = true;
836			}
837		}
838		else {
839			atleastone = true;	/* to match SRCHFS_MATCHFILES */
840		}
841	}
842	/*
843	 * Check the directory attributes
844	 */
845	else if (rec->recordType == kHFSFolderRecord ||
846	         rec->recordType == kHFSPlusFolderRecord) {
847		if ((attrList->dirattr & ~ATTR_DIR_VALIDMASK) != 0) {	/* attr we do know about  */
848			matched = false;
849			goto TestDone;
850		}
851		else if ((attrList->dirattr & ATTR_DIR_VALIDMASK) != 0) {
852		searchAttributes = attrList->dirattr;
853
854		/* Directory valence */
855		if ( searchAttributes & ATTR_DIR_ENTRYCOUNT ) {
856			matched = CompareRange(c_attr.ca_entries,
857					searchInfo1->d.numFiles,
858					searchInfo2->d.numFiles );
859			if (matched == false) goto TestDone;
860				atleastone = true;
861			}
862		}
863		else {
864			atleastone = true;		/* to match SRCHFS_MATCHDIRS */
865		}
866	}
867
868	/*
869	 * Check the common attributes
870	 */
871	searchAttributes = attrList->commonattr;
872	if ( (searchAttributes & ATTR_CMN_VALIDMASK) != 0 ) {
873		/* node ID */
874		if ( searchAttributes & ATTR_CMN_OBJID ) {
875			matched = CompareRange(c_attr.ca_fileid,
876					searchInfo1->nodeID,
877					searchInfo2->nodeID );
878			if (matched == false) goto TestDone;
879			atleastone = true;
880		}
881
882		/* Parent ID */
883		if ( searchAttributes & ATTR_CMN_PAROBJID ) {
884			HFSCatalogNodeID parentID;
885
886			if (isHFSPlus)
887				parentID = key->hfsPlus.parentID;
888			else
889				parentID = key->hfs.parentID;
890
891			matched = CompareRange(parentID, searchInfo1->parentDirID,
892					searchInfo2->parentDirID );
893			if (matched == false) goto TestDone;
894			atleastone = true;
895		}
896
897		/* Finder Info & Extended Finder Info where extFinderInfo is last 32 bytes */
898		if ( searchAttributes & ATTR_CMN_FNDRINFO ) {
899			u_int32_t *thisValue;
900			thisValue = (u_int32_t *) &c_attr.ca_finderinfo;
901
902			/*
903			 * Note: ioFlFndrInfo and ioDrUsrWds have the same offset in search info, so
904			 * no need to test the object type here.
905			 */
906			matched = CompareMasked(thisValue,
907					(u_int32_t *)&searchInfo1->finderInfo,
908					(u_int32_t *) &searchInfo2->finderInfo, 8);
909			if (matched == false) goto TestDone;
910			atleastone = true;
911		}
912
913		/* Create date */
914		if ( searchAttributes & ATTR_CMN_CRTIME ) {
915			matched = CompareRange(c_attr.ca_itime,
916					searchInfo1->creationDate.tv_sec,
917					searchInfo2->creationDate.tv_sec);
918			if (matched == false) goto TestDone;
919			atleastone = true;
920		}
921
922		/* Mod date */
923		if ( searchAttributes & ATTR_CMN_MODTIME ) {
924			matched = CompareRange(c_attr.ca_mtime,
925					searchInfo1->modificationDate.tv_sec,
926					searchInfo2->modificationDate.tv_sec);
927			if (matched == false) goto TestDone;
928			atleastone = true;
929		}
930
931		/* Change Time */
932		if ( searchAttributes & ATTR_CMN_CHGTIME ) {
933			matched = CompareRange(c_attr.ca_ctime,
934					searchInfo1->changeDate.tv_sec,
935					searchInfo2->changeDate.tv_sec);
936			if (matched == false) goto TestDone;
937			atleastone = true;
938		}
939
940		/* Access date */
941		if ( searchAttributes & ATTR_CMN_ACCTIME ) {
942			matched = CompareRange(c_attr.ca_atime,
943					searchInfo1->accessDate.tv_sec,
944					searchInfo2->accessDate.tv_sec);
945			if (matched == false) goto TestDone;
946			atleastone = true;
947		}
948
949		/* Backup date */
950		if ( searchAttributes & ATTR_CMN_BKUPTIME ) {
951			matched = CompareRange(c_attr.ca_btime,
952					searchInfo1->lastBackupDate.tv_sec,
953					searchInfo2->lastBackupDate.tv_sec);
954			if (matched == false) goto TestDone;
955			atleastone = true;
956		}
957
958		/* User ID */
959		if ( searchAttributes & ATTR_CMN_OWNERID ) {
960			matched = CompareRange(c_attr.ca_uid,
961					searchInfo1->uid, searchInfo2->uid);
962			if (matched == false) goto TestDone;
963			atleastone = true;
964		}
965
966		/* Group ID */
967		if ( searchAttributes & ATTR_CMN_GRPID ) {
968			matched = CompareRange(c_attr.ca_gid,
969					searchInfo1->gid, searchInfo2->gid);
970			if (matched == false) goto TestDone;
971			atleastone = true;
972		}
973
974		/* mode */
975		if ( searchAttributes & ATTR_CMN_ACCESSMASK ) {
976			matched = CompareRange((u_int32_t)c_attr.ca_mode,
977					(u_int32_t)searchInfo1->mask,
978					(u_int32_t)searchInfo2->mask);
979			if (matched == false) goto TestDone;
980			atleastone = true;
981		}
982	}
983
984	/* If we got here w/o matching any, then set to false */
985	if (! atleastone)
986		matched = false;
987
988TestDone:
989	/*
990	 * Finally, determine whether we need to negate the sense of the match
991	 * (i.e. find all objects that DON'T match).
992	 */
993	if ( searchBits & SRCHFS_NEGATEPARAMS )
994		matched = !matched;
995
996	return( matched );
997}
998
999
1000/*
1001 * Adds another record to the packed array for output
1002 */
1003static int
1004InsertMatch(struct hfsmount *hfsmp, uio_t a_uio, CatalogRecord *rec,
1005            CatalogKey *key, struct attrlist *returnAttrList,
1006            void *attributesBuffer, void *variableBuffer, u_long * nummatches)
1007{
1008	int err;
1009	void *rovingAttributesBuffer;
1010	void *rovingVariableBuffer;
1011	u_long packedBufferSize;
1012	struct attrblock attrblk;
1013	struct cat_desc c_desc;
1014	struct cat_attr c_attr;
1015	struct cat_fork datafork;
1016	struct cat_fork rsrcfork;
1017
1018	bzero(&c_desc, sizeof(c_desc));
1019	bzero(&c_attr, sizeof(c_attr));
1020	rovingAttributesBuffer = (char*)attributesBuffer + sizeof(u_long); /* Reserve space for length field */
1021	rovingVariableBuffer = variableBuffer;
1022
1023	/* Convert catalog record into cat_attr format. */
1024	cat_convertattr(hfsmp, rec, &c_attr, &datafork, &rsrcfork);
1025
1026	/* Hide our private meta data directories */
1027	if (c_attr.ca_fileid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
1028	    c_attr.ca_fileid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
1029		err = 0;
1030		goto exit;
1031	}
1032
1033	/* Hide the private journal files */
1034	if (hfsmp->jnl &&
1035	    ((c_attr.ca_fileid == hfsmp->hfs_jnlfileid) ||
1036	     (c_attr.ca_fileid == hfsmp->hfs_jnlinfoblkid))) {
1037		err = 0;
1038		goto exit;
1039	}
1040
1041	if (returnAttrList->commonattr & ATTR_CMN_NAME) {
1042		cat_convertkey(hfsmp, key, rec, &c_desc);
1043	} else {
1044		c_desc.cd_cnid = c_attr.ca_fileid;
1045		if (hfsmp->hfs_flags & HFS_STANDARD)
1046			c_desc.cd_parentcnid = key->hfs.parentID;
1047		else
1048			c_desc.cd_parentcnid = key->hfsPlus.parentID;
1049	}
1050
1051	attrblk.ab_attrlist = returnAttrList;
1052	attrblk.ab_attrbufpp = &rovingAttributesBuffer;
1053	attrblk.ab_varbufpp = &rovingVariableBuffer;
1054	attrblk.ab_flags = 0;
1055	attrblk.ab_blocksize = 0;
1056	attrblk.ab_context = vfs_context_current();
1057
1058	hfs_packattrblk(&attrblk, hfsmp, NULL, &c_desc, &c_attr, &datafork, &rsrcfork, current_proc());
1059
1060	packedBufferSize = (char*)rovingVariableBuffer - (char*)attributesBuffer;
1061
1062	if ( packedBufferSize > uio_resid(a_uio) )
1063		return( errSearchBufferFull );
1064
1065   	(* nummatches)++;
1066
1067	*((u_long *)attributesBuffer) = packedBufferSize;	/* Store length of fixed + var block */
1068
1069	err = uiomove( (caddr_t)attributesBuffer, packedBufferSize, a_uio );	/* XXX should be packedBufferSize */
1070exit:
1071	cat_releasedesc(&c_desc);
1072
1073	return( err );
1074}
1075
1076
1077static int
1078UnpackSearchAttributeBlock( struct hfsmount *hfsmp, struct attrlist	*alist, searchinfospec_t *searchInfo, void *attributeBuffer )
1079{
1080	attrgroup_t		a;
1081	u_long			bufferSize;
1082	boolean_t       is_64_bit;
1083
1084    DBG_ASSERT(searchInfo != NULL);
1085
1086    is_64_bit = proc_is64bit(current_proc());
1087
1088    bufferSize = *((u_int32_t *)attributeBuffer);
1089	if (bufferSize == 0)
1090		return (EINVAL);	/* XXX -DJB is a buffer size of zero ever valid for searchfs? */
1091
1092	attributeBuffer = (u_int32_t *)attributeBuffer + 1; /* advance past the size */
1093
1094	/*
1095	 * UnPack common attributes
1096	 */
1097	a = alist->commonattr;
1098	if ( a != 0 ) {
1099		if ( a & ATTR_CMN_NAME ) {
1100			char *s;
1101			u_int32_t len;
1102
1103            s = (char*) attributeBuffer + ((attrreference_t *) attributeBuffer)->attr_dataoffset;
1104            len = ((attrreference_t *) attributeBuffer)->attr_length;
1105
1106			if (len > sizeof(searchInfo->name))
1107				return (EINVAL);
1108
1109			if (hfsmp->hfs_flags & HFS_STANDARD) {
1110				/* Convert name to pascal string to match HFS B-Tree names */
1111
1112				if (len > 0) {
1113					if (utf8_to_hfs(HFSTOVCB(hfsmp), len-1, (u_char *)s, (u_char*)searchInfo->name) != 0)
1114						return (EINVAL);
1115
1116					searchInfo->nameLength = searchInfo->name[0];
1117				} else {
1118					searchInfo->name[0] = searchInfo->nameLength = 0;
1119				}
1120				attributeBuffer = (attrreference_t *)attributeBuffer + 1;
1121			} else {
1122				size_t ucslen;
1123				/* Convert name to Unicode to match HFS Plus B-Tree names */
1124
1125				if (len > 0) {
1126					if (utf8_decodestr((u_int8_t *)s, len-1, (UniChar*)searchInfo->name, &ucslen,
1127					    sizeof(searchInfo->name), ':', UTF_DECOMPOSED | UTF_ESCAPE_ILLEGAL))
1128						return (EINVAL);
1129
1130					searchInfo->nameLength = ucslen / sizeof(UniChar);
1131				} else {
1132					searchInfo->nameLength = 0;
1133				}
1134				attributeBuffer = (attrreference_t *)attributeBuffer + 1;
1135			}
1136		}
1137		if ( a & ATTR_CMN_OBJID ) {
1138			searchInfo->nodeID = ((fsobj_id_t *) attributeBuffer)->fid_objno;	/* ignore fid_generation */
1139			attributeBuffer = (fsobj_id_t *)attributeBuffer + 1;
1140		}
1141		if ( a & ATTR_CMN_PAROBJID ) {
1142			searchInfo->parentDirID = ((fsobj_id_t *) attributeBuffer)->fid_objno;  /* ignore fid_generation */
1143			attributeBuffer = (fsobj_id_t *)attributeBuffer + 1;
1144		}
1145
1146		if ( a & ATTR_CMN_CRTIME ) {
1147            if (is_64_bit) {
1148                struct user_timespec tmp;
1149                tmp = *((struct user_timespec *)attributeBuffer);
1150                searchInfo->creationDate.tv_sec = (time_t)tmp.tv_sec;
1151                searchInfo->creationDate.tv_nsec = tmp.tv_nsec;
1152				attributeBuffer = (struct user_timespec *)attributeBuffer + 1;
1153            }
1154            else {
1155                searchInfo->creationDate = *((struct timespec *)attributeBuffer);
1156				attributeBuffer = (struct timespec *)attributeBuffer + 1;
1157            }
1158		}
1159		if ( a & ATTR_CMN_MODTIME ) {
1160            if (is_64_bit) {
1161                struct user_timespec tmp;
1162                tmp = *((struct user_timespec *)attributeBuffer);
1163                searchInfo->modificationDate.tv_sec = (time_t)tmp.tv_sec;
1164                searchInfo->modificationDate.tv_nsec = tmp.tv_nsec;
1165				attributeBuffer = (struct user_timespec *)attributeBuffer + 1;
1166            }
1167            else {
1168                searchInfo->modificationDate = *((struct timespec *)attributeBuffer);
1169				attributeBuffer = (struct timespec *)attributeBuffer + 1;
1170            }
1171		}
1172		if ( a & ATTR_CMN_CHGTIME ) {
1173            if (is_64_bit) {
1174                struct user_timespec tmp;
1175                tmp = *((struct user_timespec *)attributeBuffer);
1176                searchInfo->changeDate.tv_sec = (time_t)tmp.tv_sec;
1177                searchInfo->changeDate.tv_nsec = tmp.tv_nsec;
1178				attributeBuffer = (struct user_timespec *)attributeBuffer + 1;
1179            }
1180            else {
1181                searchInfo->changeDate = *((struct timespec *)attributeBuffer);
1182				attributeBuffer = (struct timespec *)attributeBuffer + 1;
1183            }
1184		}
1185		if ( a & ATTR_CMN_ACCTIME ) {
1186            if (is_64_bit) {
1187                struct user_timespec tmp;
1188                tmp = *((struct user_timespec *)attributeBuffer);
1189                searchInfo->accessDate.tv_sec = (time_t)tmp.tv_sec;
1190                searchInfo->accessDate.tv_nsec = tmp.tv_nsec;
1191				attributeBuffer = (struct user_timespec *)attributeBuffer + 1;
1192            }
1193            else {
1194                searchInfo->accessDate = *((struct timespec *)attributeBuffer);
1195				attributeBuffer = (struct timespec *)attributeBuffer + 1;
1196            }
1197		}
1198		if ( a & ATTR_CMN_BKUPTIME ) {
1199            if (is_64_bit) {
1200                struct user_timespec tmp;
1201                tmp = *((struct user_timespec *)attributeBuffer);
1202                searchInfo->lastBackupDate.tv_sec = (time_t)tmp.tv_sec;
1203                searchInfo->lastBackupDate.tv_nsec = tmp.tv_nsec;
1204				attributeBuffer = (struct user_timespec *)attributeBuffer + 1;
1205            }
1206            else {
1207                searchInfo->lastBackupDate = *((struct timespec *)attributeBuffer);
1208				attributeBuffer = (struct timespec *)attributeBuffer + 1;
1209            }
1210		}
1211		if ( a & ATTR_CMN_FNDRINFO ) {
1212			bcopy( attributeBuffer, searchInfo->finderInfo, sizeof(searchInfo->finderInfo) );
1213			attributeBuffer = (u_int8_t *)attributeBuffer + 32;
1214		}
1215		if ( a & ATTR_CMN_OWNERID ) {
1216			searchInfo->uid = *((uid_t *)attributeBuffer);
1217			attributeBuffer = (uid_t *)attributeBuffer + 1;
1218		}
1219		if ( a & ATTR_CMN_GRPID ) {
1220			searchInfo->gid = *((gid_t *)attributeBuffer);
1221			attributeBuffer = (gid_t *)attributeBuffer + 1;
1222		}
1223		if ( a & ATTR_CMN_ACCESSMASK ) {
1224			searchInfo->mask = *((mode_t *)attributeBuffer);
1225			attributeBuffer = (mode_t *)attributeBuffer + 1;
1226		}
1227		if ( a & ATTR_CMN_FILEID ) {
1228			searchInfo->nodeID = (u_int32_t)*((u_int64_t *) attributeBuffer);
1229			attributeBuffer = (u_int64_t *)attributeBuffer + 1;
1230		}
1231		if ( a & ATTR_CMN_PARENTID ) {
1232			searchInfo->parentDirID = (u_int32_t)*((u_int64_t *) attributeBuffer);
1233			attributeBuffer = (u_int64_t *)attributeBuffer + 1;
1234		}
1235	}
1236
1237	a = alist->dirattr;
1238	if ( a != 0 ) {
1239		if ( a & ATTR_DIR_ENTRYCOUNT ) {
1240			searchInfo->d.numFiles = *((u_int32_t *)attributeBuffer);
1241			attributeBuffer = (u_int32_t *)attributeBuffer + 1;
1242		}
1243	}
1244
1245	a = alist->fileattr;
1246	if ( a != 0 ) {
1247		if ( a & ATTR_FILE_DATALENGTH ) {
1248			searchInfo->f.dataLogicalLength = *((off_t *)attributeBuffer);
1249			attributeBuffer = (off_t *)attributeBuffer + 1;
1250		}
1251		if ( a & ATTR_FILE_DATAALLOCSIZE ) {
1252			searchInfo->f.dataPhysicalLength = *((off_t *)attributeBuffer);
1253			attributeBuffer = (off_t *)attributeBuffer + 1;
1254		}
1255		if ( a & ATTR_FILE_RSRCLENGTH ) {
1256			searchInfo->f.resourceLogicalLength = *((off_t *)attributeBuffer);
1257			attributeBuffer = (off_t *)attributeBuffer + 1;
1258		}
1259		if ( a & ATTR_FILE_RSRCALLOCSIZE ) {
1260			searchInfo->f.resourcePhysicalLength = *((off_t *)attributeBuffer);
1261			attributeBuffer = (off_t *)attributeBuffer + 1;
1262		}
1263	}
1264
1265	return (0);
1266}
1267
1268