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