2 * Copyright (c) 1997-2013 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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,
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
43 #include <mach/machine/vm_types.h>
44 #include <sys/vnode.h>
45 #include <sys/malloc.h>
46 #include <sys/signalvar.h>
48 #include <sys/utfconv.h>
49 #include <sys/kauth.h>
50 #include <sys/vnode_internal.h>
51 #include <sys/mount_internal.h>
54 #include <security/mac_framework.h>
59 #include "hfs_catalog.h"
60 #include "hfs_attrlist.h"
61 #include "hfs_endian.h"
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"
71 /* Search criterea. */
72 struct directoryInfoSpec
79 off_t dataLogicalLength
;
80 off_t dataPhysicalLength
;
81 off_t resourceLogicalLength
;
82 off_t resourcePhysicalLength
;
87 u_char name
[kHFSPlusMaxFileNameBytes
];
89 char attributes
; // see IM:Files 2-100
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];
101 struct fileInfoSpec f
;
102 struct directoryInfoSpec d
;
104 typedef struct searchinfospec searchinfospec_t
;
106 static void ResolveHardlink(struct hfsmount
*hfsmp
, HFSPlusCatalogFile
*recp
);
109 static int UnpackSearchAttributeBlock(struct hfsmount
*hfsmp
, struct attrlist
*alist
,
110 searchinfospec_t
*searchInfo
, void *attributeBuffer
, int firstblock
);
112 static int CheckCriteria( ExtendedVCB
*vcb
,
114 struct attrlist
*attrList
,
117 searchinfospec_t
*searchInfo1
,
118 searchinfospec_t
*searchInfo2
,
119 struct vfs_context
*ctx
);
121 static int CheckAccess(ExtendedVCB
*vcb
, u_long searchBits
, CatalogKey
*key
, struct vfs_context
*ctx
);
123 static 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
);
128 static Boolean
CompareRange(u_long val
, u_long low
, u_long high
);
129 static Boolean
CompareWideRange(u_int64_t val
, u_int64_t low
, u_int64_t high
);
131 static Boolean
CompareRange( u_long val
, u_long low
, u_long high
)
133 return( (val
>= low
) && (val
<= high
) );
136 static Boolean
CompareWideRange( u_int64_t val
, u_int64_t low
, u_int64_t high
)
138 return( (val
>= low
) && (val
<= high
) );
140 //#define CompareRange(val, low, high) ((val >= low) && (val <= high))
143 /************************************************************************/
144 /* Entry for searchfs() */
145 /************************************************************************/
147 #define errSearchBufferFull 101 /* Internal search errors */
156 IN kauth_cred_t cred;
163 struct vnop_searchfs_args
*ap
; /*
164 struct vnodeop_desc *a_desc;
166 void *a_searchparams1;
167 void *a_searchparams2;
168 struct attrlist *a_searchattrs;
170 struct timeval *a_timelimit;
171 struct attrlist *a_returnattrs;
172 u_long *a_nummatches;
176 struct searchstate *a_searchstate;
177 vfs_context_t a_context;
180 ExtendedVCB
*vcb
= VTOVCB(ap
->a_vp
);
181 struct hfsmount
*hfsmp
;
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();
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;
201 boolean_t timerExpired
= FALSE
;
202 boolean_t needThrottle
= FALSE
;
204 /* XXX Parameter check a_searchattrs? */
206 *(ap
->a_nummatches
) = 0;
208 if (ap
->a_options
& ~SRCHFS_VALIDOPTIONSMASK
) {
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).
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)) {
226 /* SRCHFS_SKIPLINKS requires root access.
227 * This option cannot be used with either
228 * the ATTR_CMN_NAME or ATTR_CMN_PAROBJID
231 if (ap
->a_options
& SRCHFS_SKIPLINKS
) {
234 attrs
= ap
->a_searchattrs
->commonattr
| ap
->a_returnattrs
->commonattr
;
235 if (attrs
& (ATTR_CMN_NAME
| ATTR_CMN_PAROBJID
)) {
239 if ((err
= vfs_context_suser(ap
->a_context
))) {
244 // If both 32-bit and 64-bit parent ids or file ids are given
245 // then return an error.
247 attrgroup_t test_attrs
=ap
->a_searchattrs
->commonattr
;
249 if (((test_attrs
& ATTR_CMN_OBJID
) && (test_attrs
& ATTR_CMN_FILEID
)) ||
250 ((test_attrs
& ATTR_CMN_PARENTID
) && (test_attrs
& ATTR_CMN_PAROBJID
))) {
254 if (uio_resid(ap
->a_uio
) <= 0) {
258 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
259 hfsmp
= VTOHFS(ap
->a_vp
);
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
;
268 /* UnPack the search boundries, searchInfo1, searchInfo2 */
269 err
= UnpackSearchAttributeBlock(hfsmp
, ap
->a_searchattrs
,
270 &searchInfo1
, ap
->a_searchparams1
, 1);
274 err
= UnpackSearchAttributeBlock(hfsmp
, ap
->a_searchattrs
,
275 &searchInfo2
, ap
->a_searchparams2
, 0);
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
;
285 fixedBlockSize
= sizeof(u_int32_t
) + hfs_attrblksize(ap
->a_returnattrs
); /* u_int32_t for length word */
287 eachReturnBufferSize
= fixedBlockSize
;
289 if ( ap
->a_returnattrs
->commonattr
& ATTR_CMN_NAME
) /* XXX should be more robust! */
290 eachReturnBufferSize
+= kHFSPlusMaxFileNameBytes
+ 1;
292 MALLOC( attributesBuffer
, void *, eachReturnBufferSize
, M_TEMP
, M_WAITOK
);
293 if (attributesBuffer
== NULL
) {
295 goto ExitThisRoutine
;
297 bzero(attributesBuffer
, eachReturnBufferSize
);
298 variableBuffer
= (void*)((char*) attributesBuffer
+ fixedBlockSize
);
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.
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
);
308 if ((err
= vslock(user_start
, user_len
)) != 0) {
310 goto ExitThisRoutine
;
314 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
316 catalogFCB
= GetFileControlBlock(vcb
->catalogRefNum
);
317 myCurrentKeyPtr
= NULL
;
318 myCurrentDataPtr
= NULL
;
319 myCatPositionPtr
= (CatPosition
*)ap
->a_searchstate
;
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
);
326 hfs_systemfile_unlock(hfsmp
, lockflags
);
327 hfs_flush(hfsmp
, HFS_FLUSH_JOURNAL
);
328 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
331 ap
->a_options
&= ~SRCHFS_START
;
332 bzero((caddr_t
)myCatPositionPtr
, sizeof(*myCatPositionPtr
));
333 err
= BTScanInitialize(catalogFCB
, 0, 0, 0, kCatSearchBufferSize
, &myBTScanState
);
335 hfs_systemfile_unlock(hfsmp
, lockflags
);
336 goto ExitThisRoutine
;
339 /* Resuming a search. */
340 err
= BTScanInitialize(catalogFCB
, myCatPositionPtr
->nextNode
,
341 myCatPositionPtr
->nextRecord
,
342 myCatPositionPtr
->recordsFound
,
343 kCatSearchBufferSize
,
345 /* Make sure Catalog hasn't changed. */
347 && myCatPositionPtr
->writeCount
!= myBTScanState
.btcb
->writeCount
) {
348 myCatPositionPtr
->writeCount
= myBTScanState
.btcb
->writeCount
;
349 err
= EBUSY
; /* catChangedErr */
352 hfs_systemfile_unlock(hfsmp
, lockflags
);
355 goto ExitThisRoutine
;
357 if (throttle_get_io_policy(&ut
) == IOPOL_THROTTLE
)
360 * Check all the catalog btree records...
361 * return the attributes for matching items
364 struct timeval myCurrentTime
;
365 struct timeval myElapsedTime
;
367 err
= BTScanNextRecord(&myBTScanState
, timerExpired
,
368 (void **)&myCurrentKeyPtr
, (void **)&myCurrentDataPtr
,
373 /* Resolve any hardlinks */
374 if (isHFSPlus
&& (ap
->a_options
& SRCHFS_SKIPLINKS
) == 0) {
375 ResolveHardlink(vcb
, (HFSPlusCatalogFile
*)myCurrentDataPtr
);
377 if (CheckCriteria( vcb
, ap
->a_options
, ap
->a_searchattrs
, myCurrentDataPtr
,
378 myCurrentKeyPtr
, &searchInfo1
, &searchInfo2
, ap
->a_context
)
379 && CheckAccess(vcb
, ap
->a_options
, myCurrentKeyPtr
, ap
->a_context
)) {
380 err
= InsertMatch(hfsmp
, ap
->a_uio
, myCurrentDataPtr
,
381 myCurrentKeyPtr
, ap
->a_returnattrs
,
382 attributesBuffer
, variableBuffer
, ap
->a_nummatches
);
385 * The last match didn't fit so come back
386 * to this record on the next trip.
388 --myBTScanState
.recordsFound
;
389 --myBTScanState
.recordNum
;
393 if (*(ap
->a_nummatches
) >= ap
->a_maxmatches
)
396 if (timerExpired
== FALSE
) {
398 * Check our elapsed time and bail if we've hit the max.
399 * The idea here is to throttle the amount of time we
400 * spend in the kernel.
402 microuptime(&myCurrentTime
);
403 timersub(&myCurrentTime
, &myBTScanState
.startTime
, &myElapsedTime
);
405 * Note: assumes kMaxMicroSecsInKernel is less than 1,000,000
407 if (myElapsedTime
.tv_sec
> 0
408 || myElapsedTime
.tv_usec
>= searchTime
) {
410 } else if (needThrottle
== TRUE
) {
411 if (throttle_io_will_be_throttled(ut
->uu_lowpri_window
, HFSTOVFS(hfsmp
)))
417 /* Update catalog position */
418 myCatPositionPtr
->writeCount
= myBTScanState
.btcb
->writeCount
;
420 BTScanTerminate(&myBTScanState
, &myCatPositionPtr
->nextNode
,
421 &myCatPositionPtr
->nextRecord
,
422 &myCatPositionPtr
->recordsFound
);
424 if ( err
== E_NONE
) {
425 err
= EAGAIN
; /* signal to the user to call searchfs again */
426 } else if ( err
== errSearchBufferFull
) {
427 if ( *(ap
->a_nummatches
) > 0 )
431 } else if ( err
== btNotFound
) {
432 err
= E_NONE
; /* the entire disk has been searched */
433 } else if ( err
== fsBTTimeOutErr
) {
438 if (attributesBuffer
)
439 FREE(attributesBuffer
, M_TEMP
);
442 vsunlock(user_start
, user_len
, TRUE
);
445 return (MacToVFSError(err
));
450 ResolveHardlink(struct hfsmount
*hfsmp
, HFSPlusCatalogFile
*recp
)
452 u_int32_t type
, creator
;
455 time_t filecreatedate
;
457 if (recp
->recordType
!= kHFSPlusFileRecord
) {
460 type
= SWAP_BE32(recp
->userInfo
.fdType
);
461 creator
= SWAP_BE32(recp
->userInfo
.fdCreator
);
462 filecreatedate
= to_bsd_time(recp
->createDate
);
464 if ((type
== kHardLinkFileType
&& creator
== kHFSPlusCreator
) &&
465 (filecreatedate
== (time_t)hfsmp
->hfs_itime
||
466 filecreatedate
== (time_t)hfsmp
->hfs_metadata_createdate
)) {
468 } else if ((type
== kHFSAliasType
&& creator
== kHFSAliasCreator
) &&
469 (recp
->flags
& kHFSHasLinkChainMask
) &&
470 (filecreatedate
== (time_t)hfsmp
->hfs_itime
||
471 filecreatedate
== (time_t)hfsmp
->hfs_metadata_createdate
)) {
475 if (isfilelink
|| isdirlink
) {
479 /* Export link's cnid (a unique value) instead of inode's cnid */
480 saved_cnid
= recp
->fileID
;
481 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
483 (void) cat_resolvelink(hfsmp
, recp
->hl_linkReference
, isdirlink
, recp
);
485 recp
->fileID
= saved_cnid
;
486 hfs_systemfile_unlock(hfsmp
, lockflags
);
492 CompareMasked(const u_int32_t
*thisValue
, const u_int32_t
*compareData
,
493 const u_int32_t
*compareMask
, u_int32_t count
)
498 matched
= true; /* Assume it will all match */
500 for (i
=0; i
<count
; i
++) {
501 if (((*thisValue
++ ^ *compareData
++) & *compareMask
++) != 0) {
512 ComparePartialUnicodeName (register ConstUniCharArrayPtr str
, register ItemCount s_len
,
513 register ConstUniCharArrayPtr find
, register ItemCount f_len
, int caseSensitive
)
515 if (f_len
== 0 || s_len
== 0) {
523 } while (UnicodeBinaryCompare(str
++, f_len
, find
, f_len
) != 0);
529 } while (FastUnicodeCompare(str
++, f_len
, find
, f_len
) != 0);
537 ComparePartialPascalName ( register ConstStr31Param str
, register ConstStr31Param find
)
539 register u_char s_len
= str
[0];
540 register u_char f_len
= find
[0];
541 register u_char
*tsp
;
544 if (f_len
== 0 || s_len
== 0)
547 bcopy(str
, tmpstr
, s_len
+ 1);
550 while (s_len
-- >= f_len
) {
553 if (FastRelString(tsp
++, find
) == 0)
563 * Check to see if caller has access rights to this item
567 CheckAccess(ExtendedVCB
*theVCBPtr
, u_long searchBits
, CatalogKey
*theKeyPtr
, struct vfs_context
*ctx
)
572 HFSCatalogNodeID myNodeID
;
574 struct FndrDirInfo
*finfop
;
575 struct vnode
* vp
= NULL
;
577 myResult
= 0; /* default to "no access" */
579 if (!vfs_context_suser(ctx
)) {
580 myResult
= 1; /* allow access */
581 goto ExitThisRoutine
; /* root always has access */
584 hfsmp
= VCBTOHFS( theVCBPtr
);
585 isHFSPlus
= ( theVCBPtr
->vcbSigWord
== kHFSPlusSigWord
);
587 myNodeID
= theKeyPtr
->hfsPlus
.parentID
;
590 myNodeID
= theKeyPtr
->hfs
.parentID
;
593 while ( myNodeID
>= kRootDirID
) {
596 /* now go get catalog data for this directory */
597 myErr
= hfs_vget(hfsmp
, myNodeID
, &vp
, 0, 0);
599 goto ExitThisRoutine
; /* no access */
603 finfop
= (struct FndrDirInfo
*)&cp
->c_attr
.ca_finderinfo
[0];
605 if ( searchBits
& SRCHFS_SKIPPACKAGES
) {
606 if ( (SWAP_BE16(finfop
->frFlags
) & kHasBundle
)
607 || (cp
->c_desc
.cd_nameptr
!= NULL
608 && is_package_name((const char *)cp
->c_desc
.cd_nameptr
, cp
->c_desc
.cd_namelen
)) ) {
610 goto ExitThisRoutine
;
614 if ( searchBits
& SRCHFS_SKIPINAPPROPRIATE
) {
615 if ( cp
->c_parentcnid
== kRootDirID
&& cp
->c_desc
.cd_nameptr
!= NULL
&&
616 vn_searchfs_inappropriate_name((const char *)cp
->c_desc
.cd_nameptr
, cp
->c_desc
.cd_namelen
) ) {
618 goto ExitThisRoutine
;
622 if ( (searchBits
& SRCHFS_SKIPINVISIBLE
) &&
623 (SWAP_BE16(finfop
->frFlags
) & kIsInvisible
) ) {
625 goto ExitThisRoutine
;
628 myNodeID
= cp
->c_parentcnid
; /* move up the hierarchy */
629 hfs_unlock(VTOC(vp
));
632 if (vp
->v_type
== VDIR
) {
633 myErr
= mac_vnode_check_readdir(ctx
, vp
);
635 myErr
= mac_vnode_check_stat(ctx
, NOCRED
, vp
);
640 goto ExitThisRoutine
;
644 if (vp
->v_type
== VDIR
) {
645 myErr
= vnode_authorize(vp
, NULL
, (KAUTH_VNODE_SEARCH
| KAUTH_VNODE_LIST_DIRECTORY
), ctx
);
647 myErr
= vnode_authorize(vp
, NULL
, (KAUTH_VNODE_SEARCH
), ctx
);
652 goto ExitThisRoutine
; /* no access */
655 myResult
= 1; /* allow access */
659 hfs_unlock(VTOC(vp
));
667 CheckCriteria( ExtendedVCB
*vcb
,
669 struct attrlist
*attrList
,
672 searchinfospec_t
*searchInfo1
,
673 searchinfospec_t
*searchInfo2
,
674 struct vfs_context
*ctx
)
676 Boolean matched
, atleastone
;
678 attrgroup_t searchAttributes
;
679 struct cat_attr c_attr
;
680 struct cat_fork datafork
;
681 struct cat_fork rsrcfork
;
682 int force_case_sensitivity
= proc_is_forcing_hfs_case_sensitivity(vfs_context_proc(ctx
));
684 bzero(&c_attr
, sizeof(c_attr
));
685 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
687 switch (rec
->recordType
) {
690 case kHFSFolderRecord
:
691 if ( (searchBits
& SRCHFS_MATCHDIRS
) == 0 ) { /* If we are NOT searching folders */
698 if ( (searchBits
& SRCHFS_MATCHFILES
) == 0 ) { /* If we are NOT searching files */
705 case kHFSPlusFolderRecord
:
706 if ( (searchBits
& SRCHFS_MATCHDIRS
) == 0 ) { /* If we are NOT searching folders */
712 case kHFSPlusFileRecord
:
713 /* Check if hardlink links should be skipped. */
714 if (searchBits
& SRCHFS_SKIPLINKS
) {
715 cnid_t parid
= key
->hfsPlus
.parentID
;
716 HFSPlusCatalogFile
*filep
= (HFSPlusCatalogFile
*)rec
;
718 if ((SWAP_BE32(filep
->userInfo
.fdType
) == kHardLinkFileType
) &&
719 (SWAP_BE32(filep
->userInfo
.fdCreator
) == kHFSPlusCreator
)) {
720 return (false); /* skip over file link records */
721 } else if ((parid
== vcb
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
722 (filep
->bsdInfo
.special
.linkCount
== 0)) {
723 return (false); /* skip over unlinked files */
724 } else if ((SWAP_BE32(filep
->userInfo
.fdType
) == kHFSAliasType
) &&
725 (SWAP_BE32(filep
->userInfo
.fdCreator
) == kHFSAliasCreator
) &&
726 (filep
->flags
& kHFSHasLinkChainMask
)) {
727 return (false); /* skip over dir link records */
729 } else if (key
->hfsPlus
.parentID
== vcb
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
730 return (false); /* skip over private files */
731 } else if (key
->hfsPlus
.parentID
== vcb
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
732 return (false); /* skip over private files */
735 if ( (searchBits
& SRCHFS_MATCHFILES
) == 0 ) { /* If we are NOT searching files */
741 default: /* Never match a thread record or any other type. */
742 return( false ); /* Not a file or folder record, so can't search it */
745 matched
= true; /* Assume we got a match */
746 atleastone
= false; /* Dont insert unless we match at least one criteria */
748 /* First, attempt to match the name -- either partial or complete */
749 if ( attrList
->commonattr
& ATTR_CMN_NAME
) {
751 int case_sensitive
= 0;
754 * Longstanding default behavior here is to use a non-case-sensitive
755 * search, even on case-sensitive filesystems.
757 * We only force case sensitivity if the controlling process has explicitly
758 * asked for it in the proc flags, and only if they are not doing
759 * a partial name match. Consider that if you are doing a partial
760 * name match ("all files that begin with 'image'"), the likelihood is
761 * high that you would want to see all matches, even those that do not
762 * explicitly match the case.
764 if (force_case_sensitivity
) {
768 /* Check for partial/full HFS Plus name match */
770 if ( searchBits
& SRCHFS_MATCHPARTIALNAMES
) {
771 /* always use a case-INSENSITIVE search here */
772 matched
= ComparePartialUnicodeName(key
->hfsPlus
.nodeName
.unicode
,
773 key
->hfsPlus
.nodeName
.length
,
774 (UniChar
*)searchInfo1
->name
,
775 searchInfo1
->nameLength
, 0);
778 /* Full name match. Are we HFSX (case sensitive) or HFS+ ? */
779 if (case_sensitive
) {
780 matched
= (UnicodeBinaryCompare(key
->hfsPlus
.nodeName
.unicode
,
781 key
->hfsPlus
.nodeName
.length
,
782 (UniChar
*)searchInfo1
->name
,
783 searchInfo1
->nameLength
) == 0);
786 matched
= (FastUnicodeCompare(key
->hfsPlus
.nodeName
.unicode
,
787 key
->hfsPlus
.nodeName
.length
,
788 (UniChar
*)searchInfo1
->name
,
789 searchInfo1
->nameLength
) == 0);
795 /* Check for partial/full HFS name match */
797 if ( searchBits
& SRCHFS_MATCHPARTIALNAMES
)
798 matched
= ComparePartialPascalName(key
->hfs
.nodeName
, (u_char
*)searchInfo1
->name
);
799 else /* full HFS name match */
800 matched
= (FastRelString(key
->hfs
.nodeName
, (u_char
*)searchInfo1
->name
) == 0);
804 if ( matched
== false || (searchBits
& ~SRCHFS_MATCHPARTIALNAMES
) == 0 )
805 goto TestDone
; /* no match, or nothing more to compare */
810 /* Convert catalog record into cat_attr format. */
811 cat_convertattr(VCBTOHFS(vcb
), rec
, &c_attr
, &datafork
, &rsrcfork
);
813 if (searchBits
& SRCHFS_SKIPINVISIBLE
) {
816 switch (rec
->recordType
) {
818 case kHFSFolderRecord
:
820 struct FndrDirInfo
*finder_info
;
822 finder_info
= (struct FndrDirInfo
*)&c_attr
.ca_finderinfo
[0];
823 flags
= SWAP_BE16(finder_info
->frFlags
);
829 struct FndrFileInfo
*finder_info
;
831 finder_info
= (struct FndrFileInfo
*)&c_attr
.ca_finderinfo
[0];
832 flags
= SWAP_BE16(finder_info
->fdFlags
);
837 case kHFSPlusFolderRecord
:
839 struct FndrDirInfo
*finder_info
;
841 finder_info
= (struct FndrDirInfo
*)&c_attr
.ca_finderinfo
[0];
842 flags
= SWAP_BE16(finder_info
->frFlags
);
846 case kHFSPlusFileRecord
:
848 struct FndrFileInfo
*finder_info
;
850 finder_info
= (struct FndrFileInfo
*)&c_attr
.ca_finderinfo
[0];
851 flags
= SWAP_BE16(finder_info
->fdFlags
);
857 flags
= kIsInvisible
;
862 if (flags
& kIsInvisible
) {
870 /* Now that we have a record worth searching, see if it matches the search attributes */
872 if (rec
->recordType
== kHFSFileRecord
||
873 rec
->recordType
== kHFSPlusFileRecord
) {
875 if (rec
->recordType
== kHFSPlusFileRecord
) {
878 if ((attrList
->fileattr
& ~ATTR_FILE_VALIDMASK
) != 0) { /* attr we do know about */
882 else if ((attrList
->fileattr
& ATTR_FILE_VALIDMASK
) != 0) {
883 searchAttributes
= attrList
->fileattr
;
886 if ( c_attr
.ca_flags
& UF_COMPRESSED
) {
887 /* for compressed files, set the data length to the uncompressed data size */
888 if (( searchAttributes
& ATTR_FILE_DATALENGTH
) ||
889 ( searchAttributes
& ATTR_FILE_DATAALLOCSIZE
) ) {
890 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 */
891 datafork
.cf_blocks
= rsrcfork
.cf_blocks
;
894 /* treat compressed files as if their resource fork is empty */
895 if (( searchAttributes
& ATTR_FILE_RSRCLENGTH
) ||
896 ( searchAttributes
& ATTR_FILE_RSRCALLOCSIZE
) ) {
897 rsrcfork
.cf_size
= 0;
898 rsrcfork
.cf_blocks
= 0;
901 #endif /* HFS_COMPRESSION */
903 /* File logical length (data fork) */
904 if ( searchAttributes
& ATTR_FILE_DATALENGTH
) {
905 matched
= CompareWideRange(
907 searchInfo1
->f
.dataLogicalLength
,
908 searchInfo2
->f
.dataLogicalLength
);
909 if (matched
== false) goto TestDone
;
913 /* File physical length (data fork) */
914 if ( searchAttributes
& ATTR_FILE_DATAALLOCSIZE
) {
915 matched
= CompareWideRange(
916 (u_int64_t
)datafork
.cf_blocks
* (u_int64_t
)vcb
->blockSize
,
917 searchInfo1
->f
.dataPhysicalLength
,
918 searchInfo2
->f
.dataPhysicalLength
);
919 if (matched
== false) goto TestDone
;
923 /* File logical length (resource fork) */
924 if ( searchAttributes
& ATTR_FILE_RSRCLENGTH
) {
925 matched
= CompareWideRange(
927 searchInfo1
->f
.resourceLogicalLength
,
928 searchInfo2
->f
.resourceLogicalLength
);
929 if (matched
== false) goto TestDone
;
933 /* File physical length (resource fork) */
934 if ( searchAttributes
& ATTR_FILE_RSRCALLOCSIZE
) {
935 matched
= CompareWideRange(
936 (u_int64_t
)rsrcfork
.cf_blocks
* (u_int64_t
)vcb
->blockSize
,
937 searchInfo1
->f
.resourcePhysicalLength
,
938 searchInfo2
->f
.resourcePhysicalLength
);
939 if (matched
== false) goto TestDone
;
944 atleastone
= true; /* to match SRCHFS_MATCHFILES */
948 * Check the directory attributes
951 else if (rec
->recordType
== kHFSFolderRecord
||
952 rec
->recordType
== kHFSPlusFolderRecord
) {
954 else if (rec
->recordType
== kHFSPlusFolderRecord
) {
956 if ((attrList
->dirattr
& ~ATTR_DIR_VALIDMASK
) != 0) { /* attr we do know about */
960 else if ((attrList
->dirattr
& ATTR_DIR_VALIDMASK
) != 0) {
961 searchAttributes
= attrList
->dirattr
;
963 /* Directory valence */
964 if ( searchAttributes
& ATTR_DIR_ENTRYCOUNT
) {
965 matched
= CompareRange(c_attr
.ca_entries
,
966 searchInfo1
->d
.numFiles
,
967 searchInfo2
->d
.numFiles
);
968 if (matched
== false) goto TestDone
;
973 atleastone
= true; /* to match SRCHFS_MATCHDIRS */
978 * Check the common attributes
980 searchAttributes
= attrList
->commonattr
;
981 if ( (searchAttributes
& ATTR_CMN_VALIDMASK
) != 0 ) {
983 if ( searchAttributes
& ATTR_CMN_OBJID
) {
984 matched
= CompareRange(c_attr
.ca_fileid
,
986 searchInfo2
->nodeID
);
987 if (matched
== false) goto TestDone
;
992 if ( searchAttributes
& ATTR_CMN_PAROBJID
) {
993 HFSCatalogNodeID parentID
;
996 parentID
= key
->hfsPlus
.parentID
;
999 parentID
= key
->hfs
.parentID
;
1002 matched
= CompareRange(parentID
, searchInfo1
->parentDirID
,
1003 searchInfo2
->parentDirID
);
1004 if (matched
== false) goto TestDone
;
1008 /* Finder Info & Extended Finder Info where extFinderInfo is last 32 bytes */
1009 if ( searchAttributes
& ATTR_CMN_FNDRINFO
) {
1010 u_int32_t
*thisValue
;
1011 thisValue
= (u_int32_t
*) &c_attr
.ca_finderinfo
;
1014 * Note: ioFlFndrInfo and ioDrUsrWds have the same offset in search info, so
1015 * no need to test the object type here.
1017 matched
= CompareMasked(thisValue
,
1018 (u_int32_t
*)&searchInfo1
->finderInfo
,
1019 (u_int32_t
*) &searchInfo2
->finderInfo
, 8);
1020 if (matched
== false) goto TestDone
;
1025 if ( searchAttributes
& ATTR_CMN_CRTIME
) {
1026 matched
= CompareRange(c_attr
.ca_itime
,
1027 searchInfo1
->creationDate
.tv_sec
,
1028 searchInfo2
->creationDate
.tv_sec
);
1029 if (matched
== false) goto TestDone
;
1034 if ( searchAttributes
& ATTR_CMN_MODTIME
) {
1035 matched
= CompareRange(c_attr
.ca_mtime
,
1036 searchInfo1
->modificationDate
.tv_sec
,
1037 searchInfo2
->modificationDate
.tv_sec
);
1038 if (matched
== false) goto TestDone
;
1043 if ( searchAttributes
& ATTR_CMN_CHGTIME
) {
1044 matched
= CompareRange(c_attr
.ca_ctime
,
1045 searchInfo1
->changeDate
.tv_sec
,
1046 searchInfo2
->changeDate
.tv_sec
);
1047 if (matched
== false) goto TestDone
;
1052 if ( searchAttributes
& ATTR_CMN_ACCTIME
) {
1053 matched
= CompareRange(c_attr
.ca_atime
,
1054 searchInfo1
->accessDate
.tv_sec
,
1055 searchInfo2
->accessDate
.tv_sec
);
1056 if (matched
== false) goto TestDone
;
1061 if ( searchAttributes
& ATTR_CMN_BKUPTIME
) {
1062 matched
= CompareRange(c_attr
.ca_btime
,
1063 searchInfo1
->lastBackupDate
.tv_sec
,
1064 searchInfo2
->lastBackupDate
.tv_sec
);
1065 if (matched
== false) goto TestDone
;
1070 if ( searchAttributes
& ATTR_CMN_OWNERID
) {
1071 matched
= CompareRange(c_attr
.ca_uid
,
1072 searchInfo1
->uid
, searchInfo2
->uid
);
1073 if (matched
== false) goto TestDone
;
1078 if ( searchAttributes
& ATTR_CMN_GRPID
) {
1079 matched
= CompareRange(c_attr
.ca_gid
,
1080 searchInfo1
->gid
, searchInfo2
->gid
);
1081 if (matched
== false) goto TestDone
;
1086 if ( searchAttributes
& ATTR_CMN_ACCESSMASK
) {
1087 matched
= CompareRange((u_int32_t
)c_attr
.ca_mode
,
1088 (u_int32_t
)searchInfo1
->mask
,
1089 (u_int32_t
)searchInfo2
->mask
);
1090 if (matched
== false) goto TestDone
;
1095 /* If we got here w/o matching any, then set to false */
1101 * Finally, determine whether we need to negate the sense of the match
1102 * (i.e. find all objects that DON'T match).
1104 if ( searchBits
& SRCHFS_NEGATEPARAMS
)
1112 * Adds another record to the packed array for output
1115 InsertMatch(struct hfsmount
*hfsmp
, uio_t a_uio
, CatalogRecord
*rec
,
1116 CatalogKey
*key
, struct attrlist
*returnAttrList
,
1117 void *attributesBuffer
, void *variableBuffer
, uint32_t * nummatches
)
1120 void *rovingAttributesBuffer
;
1121 void *rovingVariableBuffer
;
1122 long packedBufferSize
;
1123 struct attrblock attrblk
;
1124 struct cat_desc c_desc
;
1125 struct cat_attr c_attr
;
1126 struct cat_fork datafork
;
1127 struct cat_fork rsrcfork
;
1129 bzero(&c_desc
, sizeof(c_desc
));
1130 bzero(&c_attr
, sizeof(c_attr
));
1131 rovingAttributesBuffer
= (char*)attributesBuffer
+ sizeof(u_int32_t
); /* Reserve space for length field */
1132 rovingVariableBuffer
= variableBuffer
;
1134 /* Convert catalog record into cat_attr format. */
1135 cat_convertattr(hfsmp
, rec
, &c_attr
, &datafork
, &rsrcfork
);
1137 /* Hide our private meta data directories */
1138 if (c_attr
.ca_fileid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
1139 c_attr
.ca_fileid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1144 /* Hide the private journal files */
1146 ((c_attr
.ca_fileid
== hfsmp
->hfs_jnlfileid
) ||
1147 (c_attr
.ca_fileid
== hfsmp
->hfs_jnlinfoblkid
))) {
1152 if (returnAttrList
->commonattr
& ATTR_CMN_NAME
) {
1153 err
= cat_convertkey(hfsmp
, key
, rec
, &c_desc
);
1155 /* This means that we probably had a CNID error */
1159 c_desc
.cd_cnid
= c_attr
.ca_fileid
;
1160 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0)
1161 c_desc
.cd_parentcnid
= key
->hfsPlus
.parentID
;
1164 c_desc
.cd_parentcnid
= key
->hfs
.parentID
;
1169 attrblk
.ab_attrlist
= returnAttrList
;
1170 attrblk
.ab_attrbufpp
= &rovingAttributesBuffer
;
1171 attrblk
.ab_varbufpp
= &rovingVariableBuffer
;
1172 attrblk
.ab_flags
= 0;
1173 attrblk
.ab_blocksize
= 0;
1174 attrblk
.ab_context
= vfs_context_current();
1176 hfs_packattrblk(&attrblk
, hfsmp
, NULL
, &c_desc
, &c_attr
, &datafork
, &rsrcfork
, vfs_context_current());
1178 packedBufferSize
= (char*)rovingVariableBuffer
- (char*)attributesBuffer
;
1180 if ( packedBufferSize
> uio_resid(a_uio
) )
1181 return( errSearchBufferFull
);
1185 *((u_int32_t
*)attributesBuffer
) = packedBufferSize
; /* Store length of fixed + var block */
1187 err
= uiomove( (caddr_t
)attributesBuffer
, packedBufferSize
, a_uio
);
1189 cat_releasedesc(&c_desc
);
1196 UnpackSearchAttributeBlock( struct hfsmount
*hfsmp
, struct attrlist
*alist
,
1197 searchinfospec_t
*searchInfo
, void *attributeBuffer
, int firstblock
)
1200 u_int32_t bufferSize
;
1201 boolean_t is_64_bit
;
1203 DBG_ASSERT(searchInfo
!= NULL
);
1205 is_64_bit
= proc_is64bit(current_proc());
1207 bufferSize
= *((u_int32_t
*)attributeBuffer
);
1208 if (bufferSize
== 0)
1209 return (EINVAL
); /* XXX -DJB is a buffer size of zero ever valid for searchfs? */
1211 attributeBuffer
= (u_int32_t
*)attributeBuffer
+ 1; /* advance past the size */
1214 * UnPack common attributes
1216 a
= alist
->commonattr
;
1218 if ( a
& ATTR_CMN_NAME
) {
1220 /* Only use the attrreference_t for the first searchparams */
1224 s
= (char*) attributeBuffer
+ ((attrreference_t
*) attributeBuffer
)->attr_dataoffset
;
1225 len
= ((attrreference_t
*) attributeBuffer
)->attr_length
;
1227 if (len
> sizeof(searchInfo
->name
))
1231 if ((hfsmp
->hfs_flags
& HFS_STANDARD
) == 0) {
1233 /* Convert name to Unicode to match HFS Plus B-Tree names */
1236 if (utf8_decodestr((u_int8_t
*)s
, len
-1, (UniChar
*)searchInfo
->name
, &ucslen
,
1237 sizeof(searchInfo
->name
), ':', UTF_DECOMPOSED
| UTF_ESCAPE_ILLEGAL
))
1240 searchInfo
->nameLength
= ucslen
/ sizeof(UniChar
);
1242 searchInfo
->nameLength
= 0;
1247 /* Convert name to pascal string to match HFS (Standard) B-Tree names */
1250 if (utf8_to_hfs(HFSTOVCB(hfsmp
), len
-1, (u_char
*)s
, (u_char
*)searchInfo
->name
) != 0)
1253 searchInfo
->nameLength
= searchInfo
->name
[0];
1255 searchInfo
->name
[0] = searchInfo
->nameLength
= 0;
1260 attributeBuffer
= (attrreference_t
*) attributeBuffer
+1;
1262 if ( a
& ATTR_CMN_OBJID
) {
1263 searchInfo
->nodeID
= ((fsobj_id_t
*) attributeBuffer
)->fid_objno
; /* ignore fid_generation */
1264 attributeBuffer
= (fsobj_id_t
*)attributeBuffer
+ 1;
1266 if ( a
& ATTR_CMN_PAROBJID
) {
1267 searchInfo
->parentDirID
= ((fsobj_id_t
*) attributeBuffer
)->fid_objno
; /* ignore fid_generation */
1268 attributeBuffer
= (fsobj_id_t
*)attributeBuffer
+ 1;
1271 if ( a
& ATTR_CMN_CRTIME
) {
1273 struct user64_timespec tmp
;
1274 tmp
= *((struct user64_timespec
*)attributeBuffer
);
1275 searchInfo
->creationDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1276 searchInfo
->creationDate
.tv_nsec
= tmp
.tv_nsec
;
1277 attributeBuffer
= (struct user64_timespec
*)attributeBuffer
+ 1;
1280 struct user32_timespec tmp
;
1281 tmp
= *((struct user32_timespec
*)attributeBuffer
);
1282 searchInfo
->creationDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1283 searchInfo
->creationDate
.tv_nsec
= tmp
.tv_nsec
;
1284 attributeBuffer
= (struct user32_timespec
*)attributeBuffer
+ 1;
1287 if ( a
& ATTR_CMN_MODTIME
) {
1289 struct user64_timespec tmp
;
1290 tmp
= *((struct user64_timespec
*)attributeBuffer
);
1291 searchInfo
->modificationDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1292 searchInfo
->modificationDate
.tv_nsec
= tmp
.tv_nsec
;
1293 attributeBuffer
= (struct user64_timespec
*)attributeBuffer
+ 1;
1296 struct user32_timespec tmp
;
1297 tmp
= *((struct user32_timespec
*)attributeBuffer
);
1298 searchInfo
->modificationDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1299 searchInfo
->modificationDate
.tv_nsec
= tmp
.tv_nsec
;
1300 attributeBuffer
= (struct user32_timespec
*)attributeBuffer
+ 1;
1303 if ( a
& ATTR_CMN_CHGTIME
) {
1305 struct user64_timespec tmp
;
1306 tmp
= *((struct user64_timespec
*)attributeBuffer
);
1307 searchInfo
->changeDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1308 searchInfo
->changeDate
.tv_nsec
= tmp
.tv_nsec
;
1309 attributeBuffer
= (struct user64_timespec
*)attributeBuffer
+ 1;
1312 struct user32_timespec tmp
;
1313 tmp
= *((struct user32_timespec
*)attributeBuffer
);
1314 searchInfo
->changeDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1315 searchInfo
->changeDate
.tv_nsec
= tmp
.tv_nsec
;
1316 attributeBuffer
= (struct user32_timespec
*)attributeBuffer
+ 1;
1319 if ( a
& ATTR_CMN_ACCTIME
) {
1321 struct user64_timespec tmp
;
1322 tmp
= *((struct user64_timespec
*)attributeBuffer
);
1323 searchInfo
->accessDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1324 searchInfo
->accessDate
.tv_nsec
= tmp
.tv_nsec
;
1325 attributeBuffer
= (struct user64_timespec
*)attributeBuffer
+ 1;
1328 struct user32_timespec tmp
;
1329 tmp
= *((struct user32_timespec
*)attributeBuffer
);
1330 searchInfo
->accessDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1331 searchInfo
->accessDate
.tv_nsec
= tmp
.tv_nsec
;
1332 attributeBuffer
= (struct user32_timespec
*)attributeBuffer
+ 1;
1335 if ( a
& ATTR_CMN_BKUPTIME
) {
1337 struct user64_timespec tmp
;
1338 tmp
= *((struct user64_timespec
*)attributeBuffer
);
1339 searchInfo
->lastBackupDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1340 searchInfo
->lastBackupDate
.tv_nsec
= tmp
.tv_nsec
;
1341 attributeBuffer
= (struct user64_timespec
*)attributeBuffer
+ 1;
1344 struct user32_timespec tmp
;
1345 tmp
= *((struct user32_timespec
*)attributeBuffer
);
1346 searchInfo
->lastBackupDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1347 searchInfo
->lastBackupDate
.tv_nsec
= tmp
.tv_nsec
;
1348 attributeBuffer
= (struct user32_timespec
*)attributeBuffer
+ 1;
1351 if ( a
& ATTR_CMN_FNDRINFO
) {
1352 bcopy( attributeBuffer
, searchInfo
->finderInfo
, sizeof(searchInfo
->finderInfo
) );
1353 attributeBuffer
= (u_int8_t
*)attributeBuffer
+ 32;
1355 if ( a
& ATTR_CMN_OWNERID
) {
1356 searchInfo
->uid
= *((uid_t
*)attributeBuffer
);
1357 attributeBuffer
= (uid_t
*)attributeBuffer
+ 1;
1359 if ( a
& ATTR_CMN_GRPID
) {
1360 searchInfo
->gid
= *((gid_t
*)attributeBuffer
);
1361 attributeBuffer
= (gid_t
*)attributeBuffer
+ 1;
1363 if ( a
& ATTR_CMN_ACCESSMASK
) {
1364 searchInfo
->mask
= *((mode_t
*)attributeBuffer
);
1365 attributeBuffer
= (mode_t
*)attributeBuffer
+ 1;
1367 if ( a
& ATTR_CMN_FILEID
) {
1368 searchInfo
->nodeID
= (u_int32_t
)*((u_int64_t
*) attributeBuffer
);
1369 attributeBuffer
= (u_int64_t
*)attributeBuffer
+ 1;
1371 if ( a
& ATTR_CMN_PARENTID
) {
1372 searchInfo
->parentDirID
= (u_int32_t
)*((u_int64_t
*) attributeBuffer
);
1373 attributeBuffer
= (u_int64_t
*)attributeBuffer
+ 1;
1379 if ( a
& ATTR_DIR_ENTRYCOUNT
) {
1380 searchInfo
->d
.numFiles
= *((u_int32_t
*)attributeBuffer
);
1381 attributeBuffer
= (u_int32_t
*)attributeBuffer
+ 1;
1385 a
= alist
->fileattr
;
1387 if ( a
& ATTR_FILE_DATALENGTH
) {
1388 searchInfo
->f
.dataLogicalLength
= *((off_t
*)attributeBuffer
);
1389 attributeBuffer
= (off_t
*)attributeBuffer
+ 1;
1391 if ( a
& ATTR_FILE_DATAALLOCSIZE
) {
1392 searchInfo
->f
.dataPhysicalLength
= *((off_t
*)attributeBuffer
);
1393 attributeBuffer
= (off_t
*)attributeBuffer
+ 1;
1395 if ( a
& ATTR_FILE_RSRCLENGTH
) {
1396 searchInfo
->f
.resourceLogicalLength
= *((off_t
*)attributeBuffer
);
1397 attributeBuffer
= (off_t
*)attributeBuffer
+ 1;
1399 if ( a
& ATTR_FILE_RSRCALLOCSIZE
) {
1400 searchInfo
->f
.resourcePhysicalLength
= *((off_t
*)attributeBuffer
);
1401 attributeBuffer
= (off_t
*)attributeBuffer
+ 1;
1407 #endif /* CONFIG_SEARCHFS */