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