2 * Copyright (c) 1997-2007 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>
53 #include <security/mac_framework.h>
58 #include "hfs_catalog.h"
59 #include "hfs_attrlist.h"
60 #include "hfs_endian.h"
62 #include "hfscommon/headers/FileMgrInternal.h"
63 #include "hfscommon/headers/HFSUnicodeWrappers.h"
64 #include "hfscommon/headers/BTreesPrivate.h"
65 #include "hfscommon/headers/BTreeScanner.h"
66 #include "hfscommon/headers/CatalogPrivate.h"
68 /* Search criterea. */
69 struct directoryInfoSpec
76 off_t dataLogicalLength
;
77 off_t dataPhysicalLength
;
78 off_t resourceLogicalLength
;
79 off_t resourcePhysicalLength
;
84 u_char name
[kHFSPlusMaxFileNameBytes
];
86 char attributes
; // see IM:Files 2-100
89 struct timespec creationDate
;
90 struct timespec modificationDate
;
91 struct timespec changeDate
;
92 struct timespec accessDate
;
93 struct timespec lastBackupDate
;
94 u_int8_t finderInfo
[32];
98 struct fileInfoSpec f
;
99 struct directoryInfoSpec d
;
101 typedef struct searchinfospec searchinfospec_t
;
103 static void ResolveHardlink(struct hfsmount
*hfsmp
, HFSPlusCatalogFile
*recp
);
106 static int UnpackSearchAttributeBlock(struct hfsmount
*hfsmp
, struct attrlist
*alist
,
107 searchinfospec_t
*searchInfo
, void *attributeBuffer
);
109 static int CheckCriteria( ExtendedVCB
*vcb
,
111 struct attrlist
*attrList
,
114 searchinfospec_t
*searchInfo1
,
115 searchinfospec_t
*searchInfo2
);
117 static int CheckAccess(ExtendedVCB
*vcb
, u_long searchBits
, CatalogKey
*key
, struct vfs_context
*ctx
);
119 static int InsertMatch(struct hfsmount
*hfsmp
, uio_t a_uio
, CatalogRecord
*rec
,
120 CatalogKey
*key
, struct attrlist
*returnAttrList
,
121 void *attributesBuffer
, void *variableBuffer
,
122 u_long
* nummatches
);
124 static Boolean
CompareRange(u_long val
, u_long low
, u_long high
);
125 static Boolean
CompareWideRange(u_int64_t val
, u_int64_t low
, u_int64_t high
);
127 static Boolean
CompareRange( u_long val
, u_long low
, u_long high
)
129 return( (val
>= low
) && (val
<= high
) );
132 static Boolean
CompareWideRange( u_int64_t val
, u_int64_t low
, u_int64_t high
)
134 return( (val
>= low
) && (val
<= high
) );
136 //#define CompareRange(val, low, high) ((val >= low) && (val <= high))
139 /************************************************************************/
140 /* Entry for searchfs() */
141 /************************************************************************/
143 #define errSearchBufferFull 101 /* Internal search errors */
152 IN kauth_cred_t cred;
160 struct vnop_searchfs_args
*ap
; /*
161 struct vnodeop_desc *a_desc;
163 void *a_searchparams1;
164 void *a_searchparams2;
165 struct attrlist *a_searchattrs;
167 struct timeval *a_timelimit;
168 struct attrlist *a_returnattrs;
169 u_long *a_nummatches;
173 struct searchstate *a_searchstate;
174 vfs_context_t a_context;
177 ExtendedVCB
*vcb
= VTOVCB(ap
->a_vp
);
178 struct hfsmount
*hfsmp
;
180 searchinfospec_t searchInfo1
;
181 searchinfospec_t searchInfo2
;
182 void *attributesBuffer
;
183 void *variableBuffer
;
184 u_long fixedBlockSize
;
185 u_long eachReturnBufferSize
;
186 struct proc
*p
= current_proc();
189 int timerExpired
= false;
190 CatalogKey
* myCurrentKeyPtr
;
191 CatalogRecord
* myCurrentDataPtr
;
192 CatPosition
* myCatPositionPtr
;
193 BTScanState myBTScanState
;
194 user_addr_t user_start
= 0;
195 user_size_t user_len
= 0;
199 /* XXX Parameter check a_searchattrs? */
201 *(ap
->a_nummatches
) = 0;
203 if (ap
->a_options
& ~SRCHFS_VALIDOPTIONSMASK
)
207 * Reject requests for unsupported attributes.
209 if ((ap
->a_returnattrs
->commonattr
& ~HFS_ATTR_CMN_VALID
) ||
210 (ap
->a_returnattrs
->volattr
!= 0) ||
211 (ap
->a_returnattrs
->dirattr
& ~HFS_ATTR_DIR_VALID
) ||
212 (ap
->a_returnattrs
->fileattr
& ~HFS_ATTR_FILE_VALID
) ||
213 (ap
->a_returnattrs
->forkattr
!= 0)) {
217 /* SRCHFS_SKIPLINKS requires root access.
218 * This option cannot be used with either
219 * the ATTR_CMN_NAME or ATTR_CMN_PAROBJID
222 if (ap
->a_options
& SRCHFS_SKIPLINKS
) {
225 attrs
= ap
->a_searchattrs
->commonattr
| ap
->a_returnattrs
->commonattr
;
226 if (attrs
& (ATTR_CMN_NAME
| ATTR_CMN_PAROBJID
))
228 if ((err
= vfs_context_suser(ap
->a_context
)))
232 // If both 32-bit and 64-bit parent ids or file ids are given
233 // then return an error.
235 attrgroup_t test_attrs
=ap
->a_searchattrs
->commonattr
;
237 if (((test_attrs
& ATTR_CMN_OBJID
) && (test_attrs
& ATTR_CMN_FILEID
)) ||
238 ((test_attrs
& ATTR_CMN_PARENTID
) && (test_attrs
& ATTR_CMN_PAROBJID
)))
242 if (uio_resid(ap
->a_uio
) <= 0)
245 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
246 hfsmp
= VTOHFS(ap
->a_vp
);
248 searchTime
= kMaxMicroSecsInKernel
;
249 if (ap
->a_timelimit
->tv_sec
== 0 &&
250 ap
->a_timelimit
->tv_usec
> 0 &&
251 ap
->a_timelimit
->tv_usec
< kMaxMicroSecsInKernel
) {
252 searchTime
= ap
->a_timelimit
->tv_usec
;
255 /* UnPack the search boundries, searchInfo1, searchInfo2 */
256 err
= UnpackSearchAttributeBlock(hfsmp
, ap
->a_searchattrs
,
257 &searchInfo1
, ap
->a_searchparams1
);
259 err
= UnpackSearchAttributeBlock(hfsmp
, ap
->a_searchattrs
,
260 &searchInfo2
, ap
->a_searchparams2
);
263 //shadow search bits if 64-bit file/parent ids are used
264 if (ap
->a_searchattrs
->commonattr
& ATTR_CMN_FILEID
)
265 ap
->a_searchattrs
->commonattr
|= ATTR_CMN_OBJID
;
266 if (ap
->a_searchattrs
->commonattr
& ATTR_CMN_PARENTID
)
267 ap
->a_searchattrs
->commonattr
|= ATTR_CMN_PAROBJID
;
269 fixedBlockSize
= sizeof(u_int32_t
) + hfs_attrblksize(ap
->a_returnattrs
); /* u_int32_t for length word */
271 eachReturnBufferSize
= fixedBlockSize
;
273 if ( ap
->a_returnattrs
->commonattr
& ATTR_CMN_NAME
) /* XXX should be more robust! */
274 eachReturnBufferSize
+= kHFSPlusMaxFileNameBytes
+ 1;
276 MALLOC( attributesBuffer
, void *, eachReturnBufferSize
, M_TEMP
, M_WAITOK
);
277 variableBuffer
= (void*)((char*) attributesBuffer
+ fixedBlockSize
);
279 // XXXdbg - have to lock the user's buffer so we don't fault
280 // while holding the shared catalog file lock. see the comment
281 // in hfs_readdir() for more details.
283 if (hfsmp
->jnl
&& uio_isuserspace(ap
->a_uio
)) {
284 user_start
= uio_curriovbase(ap
->a_uio
);
285 user_len
= uio_curriovlen(ap
->a_uio
);
287 if ((err
= vslock(user_start
, user_len
)) != 0) {
289 goto ExitThisRoutine
;
293 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
295 catalogFCB
= GetFileControlBlock(vcb
->catalogRefNum
);
296 myCurrentKeyPtr
= NULL
;
297 myCurrentDataPtr
= NULL
;
298 myCatPositionPtr
= (CatPosition
*)ap
->a_searchstate
;
300 if (ap
->a_options
& SRCHFS_START
) {
301 /* Starting a new search. */
302 /* Make sure the on-disk Catalog file is current */
303 (void) hfs_fsync(vcb
->catalogRefNum
, MNT_WAIT
, 0, p
);
305 hfs_systemfile_unlock(hfsmp
, lockflags
);
306 journal_flush(hfsmp
->jnl
);
307 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
310 ap
->a_options
&= ~SRCHFS_START
;
311 bzero((caddr_t
)myCatPositionPtr
, sizeof(*myCatPositionPtr
));
312 err
= BTScanInitialize(catalogFCB
, 0, 0, 0, kCatSearchBufferSize
, &myBTScanState
);
314 goto ExitThisRoutine
;
317 /* Resuming a search. */
318 err
= BTScanInitialize(catalogFCB
, myCatPositionPtr
->nextNode
,
319 myCatPositionPtr
->nextRecord
,
320 myCatPositionPtr
->recordsFound
,
321 kCatSearchBufferSize
,
323 /* Make sure Catalog hasn't changed. */
325 && myCatPositionPtr
->writeCount
!= myBTScanState
.btcb
->writeCount
) {
326 myCatPositionPtr
->writeCount
= myBTScanState
.btcb
->writeCount
;
327 err
= EBUSY
; /* catChangedErr */
330 hfs_systemfile_unlock(hfsmp
, lockflags
);
333 goto ExitThisRoutine
;
336 * Check all the catalog btree records...
337 * return the attributes for matching items
340 struct timeval myCurrentTime
;
341 struct timeval myElapsedTime
;
343 err
= BTScanNextRecord(&myBTScanState
, timerExpired
,
344 (void **)&myCurrentKeyPtr
, (void **)&myCurrentDataPtr
,
349 /* Resolve any hardlinks */
350 if (isHFSPlus
&& (ap
->a_options
& SRCHFS_SKIPLINKS
) == 0) {
351 ResolveHardlink(vcb
, (HFSPlusCatalogFile
*)myCurrentDataPtr
);
353 if (CheckCriteria( vcb
, ap
->a_options
, ap
->a_searchattrs
, myCurrentDataPtr
,
354 myCurrentKeyPtr
, &searchInfo1
, &searchInfo2
)
355 && CheckAccess(vcb
, ap
->a_options
, myCurrentKeyPtr
, ap
->a_context
)) {
356 err
= InsertMatch(hfsmp
, ap
->a_uio
, myCurrentDataPtr
,
357 myCurrentKeyPtr
, ap
->a_returnattrs
,
358 attributesBuffer
, variableBuffer
, ap
->a_nummatches
);
361 * The last match didn't fit so come back
362 * to this record on the next trip.
364 --myBTScanState
.recordsFound
;
365 --myBTScanState
.recordNum
;
369 if (*(ap
->a_nummatches
) >= ap
->a_maxmatches
)
374 * Check our elapsed time and bail if we've hit the max.
375 * The idea here is to throttle the amount of time we
376 * spend in the kernel.
378 microuptime(&myCurrentTime
);
379 timersub(&myCurrentTime
, &myBTScanState
.startTime
, &myElapsedTime
);
380 /* Note: assumes kMaxMicroSecsInKernel is less than 1,000,000 */
381 if (myElapsedTime
.tv_sec
> 0
382 || myElapsedTime
.tv_usec
>= searchTime
) {
387 /* Update catalog position */
388 myCatPositionPtr
->writeCount
= myBTScanState
.btcb
->writeCount
;
390 BTScanTerminate(&myBTScanState
, &myCatPositionPtr
->nextNode
,
391 &myCatPositionPtr
->nextRecord
,
392 &myCatPositionPtr
->recordsFound
);
394 if ( err
== E_NONE
) {
395 err
= EAGAIN
; /* signal to the user to call searchfs again */
396 } else if ( err
== errSearchBufferFull
) {
397 if ( *(ap
->a_nummatches
) > 0 )
401 } else if ( err
== btNotFound
) {
402 err
= E_NONE
; /* the entire disk has been searched */
403 } else if ( err
== fsBTTimeOutErr
) {
408 FREE( attributesBuffer
, M_TEMP
);
410 if (hfsmp
->jnl
&& user_start
) {
411 vsunlock(user_start
, user_len
, TRUE
);
414 return (MacToVFSError(err
));
419 ResolveHardlink(struct hfsmount
*hfsmp
, HFSPlusCatalogFile
*recp
)
421 u_int32_t type
, creator
;
424 time_t filecreatedate
;
426 if (recp
->recordType
!= kHFSPlusFileRecord
) {
429 type
= SWAP_BE32(recp
->userInfo
.fdType
);
430 creator
= SWAP_BE32(recp
->userInfo
.fdCreator
);
431 filecreatedate
= to_bsd_time(recp
->createDate
);
433 if ((type
== kHardLinkFileType
&& creator
== kHFSPlusCreator
) &&
434 (filecreatedate
== (time_t)hfsmp
->vcbCrDate
||
435 filecreatedate
== (time_t)hfsmp
->hfs_metadata_createdate
)) {
437 } else if ((type
== kHFSAliasType
&& creator
== kHFSAliasCreator
) &&
438 (recp
->flags
& kHFSHasLinkChainMask
) &&
439 (filecreatedate
== (time_t)hfsmp
->vcbCrDate
||
440 filecreatedate
== (time_t)hfsmp
->hfs_metadata_createdate
)) {
444 if (isfilelink
|| isdirlink
) {
448 /* Export link's cnid (a unique value) instead of inode's cnid */
449 saved_cnid
= recp
->fileID
;
450 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
452 (void) cat_resolvelink(hfsmp
, recp
->hl_linkReference
, isdirlink
, recp
);
454 recp
->fileID
= saved_cnid
;
455 hfs_systemfile_unlock(hfsmp
, lockflags
);
461 CompareMasked(const u_int32_t
*thisValue
, const u_int32_t
*compareData
,
462 const u_int32_t
*compareMask
, u_int32_t count
)
467 matched
= true; /* Assume it will all match */
469 for (i
=0; i
<count
; i
++) {
470 if (((*thisValue
++ ^ *compareData
++) & *compareMask
++) != 0) {
481 ComparePartialUnicodeName (register ConstUniCharArrayPtr str
, register ItemCount s_len
,
482 register ConstUniCharArrayPtr find
, register ItemCount f_len
)
484 if (f_len
== 0 || s_len
== 0)
490 } while (FastUnicodeCompare(str
++, f_len
, find
, f_len
) != 0);
497 ComparePartialPascalName ( register ConstStr31Param str
, register ConstStr31Param find
)
499 register u_char s_len
= str
[0];
500 register u_char f_len
= find
[0];
501 register u_char
*tsp
;
504 if (f_len
== 0 || s_len
== 0)
507 bcopy(str
, tmpstr
, s_len
+ 1);
510 while (s_len
-- >= f_len
) {
513 if (FastRelString(tsp
++, find
) == 0)
522 // Determine if a name is "inappropriate" where the definition
523 // of "inappropriate" is up to higher level execs. Currently
524 // that's limited to /System.
527 is_inappropriate_name(const char *name
, int len
)
529 const char *bad_names
[] = { "System" };
530 int bad_len
[] = { 6 };
533 for(i
=0; i
< (int) (sizeof(bad_names
) / sizeof(bad_names
[0])); i
++) {
534 if (len
== bad_len
[i
] && strncmp(name
, bad_names
[i
], strlen(bad_names
[i
]) + 1) == 0) {
539 // if we get here, no name matched
546 * Check to see if caller has access rights to this item
550 CheckAccess(ExtendedVCB
*theVCBPtr
, u_long searchBits
, CatalogKey
*theKeyPtr
, struct vfs_context
*ctx
)
555 HFSCatalogNodeID myNodeID
;
557 struct FndrDirInfo
*finfop
;
558 struct vnode
* vp
= NULL
;
560 myResult
= 0; /* default to "no access" */
562 if (!vfs_context_suser(ctx
)) {
563 myResult
= 1; /* allow access */
564 goto ExitThisRoutine
; /* root always has access */
567 hfsmp
= VCBTOHFS( theVCBPtr
);
568 isHFSPlus
= ( theVCBPtr
->vcbSigWord
== kHFSPlusSigWord
);
570 myNodeID
= theKeyPtr
->hfsPlus
.parentID
;
572 myNodeID
= theKeyPtr
->hfs
.parentID
;
574 while ( myNodeID
>= kRootDirID
) {
577 /* now go get catalog data for this directory */
578 myErr
= hfs_vget(hfsmp
, myNodeID
, &vp
, 0);
580 goto ExitThisRoutine
; /* no access */
584 finfop
= (struct FndrDirInfo
*)&cp
->c_attr
.ca_finderinfo
[0];
586 if ( searchBits
& SRCHFS_SKIPPACKAGES
) {
587 if ( (SWAP_BE16(finfop
->frFlags
) & kHasBundle
)
588 || (cp
->c_desc
.cd_nameptr
!= NULL
589 && is_package_name((const char *)cp
->c_desc
.cd_nameptr
, cp
->c_desc
.cd_namelen
)) ) {
591 goto ExitThisRoutine
;
595 if ( searchBits
& SRCHFS_SKIPINAPPROPRIATE
) {
596 if ( cp
->c_parentcnid
== kRootDirID
&& cp
->c_desc
.cd_nameptr
!= NULL
&&
597 is_inappropriate_name((const char *)cp
->c_desc
.cd_nameptr
, cp
->c_desc
.cd_namelen
) ) {
599 goto ExitThisRoutine
;
603 if ( (searchBits
& SRCHFS_SKIPINVISIBLE
) &&
604 (SWAP_BE16(finfop
->frFlags
) & kIsInvisible
) ) {
606 goto ExitThisRoutine
;
609 myNodeID
= cp
->c_parentcnid
; /* move up the hierarchy */
610 hfs_unlock(VTOC(vp
));
613 if (vp
->v_type
== VDIR
) {
614 myErr
= mac_vnode_check_readdir(ctx
, vp
);
616 myErr
= mac_vnode_check_stat(ctx
, NOCRED
, vp
);
621 goto ExitThisRoutine
;
625 if (vp
->v_type
== VDIR
) {
626 myErr
= vnode_authorize(vp
, NULL
, (KAUTH_VNODE_SEARCH
| KAUTH_VNODE_LIST_DIRECTORY
), ctx
);
628 myErr
= vnode_authorize(vp
, NULL
, (KAUTH_VNODE_SEARCH
), ctx
);
633 goto ExitThisRoutine
; /* no access */
636 myResult
= 1; /* allow access */
640 hfs_unlock(VTOC(vp
));
648 CheckCriteria( ExtendedVCB
*vcb
,
650 struct attrlist
*attrList
,
653 searchinfospec_t
*searchInfo1
,
654 searchinfospec_t
*searchInfo2
)
656 Boolean matched
, atleastone
;
658 attrgroup_t searchAttributes
;
659 struct cat_attr c_attr
;
660 struct cat_fork datafork
;
661 struct cat_fork rsrcfork
;
663 bzero(&c_attr
, sizeof(c_attr
));
664 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
666 switch (rec
->recordType
) {
667 case kHFSFolderRecord
:
668 case kHFSPlusFolderRecord
:
669 if ( (searchBits
& SRCHFS_MATCHDIRS
) == 0 ) { /* If we are NOT searching folders */
676 if ( (searchBits
& SRCHFS_MATCHFILES
) == 0 ) { /* If we are NOT searching files */
682 case kHFSPlusFileRecord
:
683 /* Check if hardlink links should be skipped. */
684 if (searchBits
& SRCHFS_SKIPLINKS
) {
685 cnid_t parid
= key
->hfsPlus
.parentID
;
686 HFSPlusCatalogFile
*filep
= (HFSPlusCatalogFile
*)rec
;
688 if ((SWAP_BE32(filep
->userInfo
.fdType
) == kHardLinkFileType
) &&
689 (SWAP_BE32(filep
->userInfo
.fdCreator
) == kHFSPlusCreator
)) {
690 return (false); /* skip over file link records */
691 } else if ((parid
== vcb
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) &&
692 (filep
->bsdInfo
.special
.linkCount
== 0)) {
693 return (false); /* skip over unlinked files */
694 } else if ((SWAP_BE32(filep
->userInfo
.fdType
) == kHFSAliasType
) &&
695 (SWAP_BE32(filep
->userInfo
.fdCreator
) == kHFSAliasCreator
) &&
696 (filep
->flags
& kHFSHasLinkChainMask
)) {
697 return (false); /* skip over dir link records */
699 } else if (key
->hfsPlus
.parentID
== vcb
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
) {
700 return (false); /* skip over private files */
701 } else if (key
->hfsPlus
.parentID
== vcb
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
702 return (false); /* skip over private files */
705 if ( (searchBits
& SRCHFS_MATCHFILES
) == 0 ) { /* If we are NOT searching files */
711 default: /* Never match a thread record or any other type. */
712 return( false ); /* Not a file or folder record, so can't search it */
715 matched
= true; /* Assume we got a match */
716 atleastone
= false; /* Dont insert unless we match at least one criteria */
718 /* First, attempt to match the name -- either partial or complete */
719 if ( attrList
->commonattr
& ATTR_CMN_NAME
) {
721 /* Check for partial/full HFS Plus name match */
723 if ( searchBits
& SRCHFS_MATCHPARTIALNAMES
) {
724 matched
= ComparePartialUnicodeName(key
->hfsPlus
.nodeName
.unicode
,
725 key
->hfsPlus
.nodeName
.length
,
726 (UniChar
*)searchInfo1
->name
,
727 searchInfo1
->nameLength
);
728 } else /* full HFS Plus name match */ {
729 matched
= (FastUnicodeCompare(key
->hfsPlus
.nodeName
.unicode
,
730 key
->hfsPlus
.nodeName
.length
,
731 (UniChar
*)searchInfo1
->name
,
732 searchInfo1
->nameLength
) == 0);
735 /* Check for partial/full HFS name match */
737 if ( searchBits
& SRCHFS_MATCHPARTIALNAMES
)
738 matched
= ComparePartialPascalName(key
->hfs
.nodeName
, (u_char
*)searchInfo1
->name
);
739 else /* full HFS name match */
740 matched
= (FastRelString(key
->hfs
.nodeName
, (u_char
*)searchInfo1
->name
) == 0);
743 if ( matched
== false || (searchBits
& ~SRCHFS_MATCHPARTIALNAMES
) == 0 )
744 goto TestDone
; /* no match, or nothing more to compare */
749 /* Convert catalog record into cat_attr format. */
750 cat_convertattr(VCBTOHFS(vcb
), rec
, &c_attr
, &datafork
, &rsrcfork
);
752 if (searchBits
& SRCHFS_SKIPINVISIBLE
) {
755 switch (rec
->recordType
) {
756 case kHFSFolderRecord
:
757 case kHFSPlusFolderRecord
: {
758 struct FndrDirInfo
*finder_info
;
760 finder_info
= (struct FndrDirInfo
*)&c_attr
.ca_finderinfo
[0];
761 flags
= SWAP_BE16(finder_info
->frFlags
);
766 case kHFSPlusFileRecord
: {
767 struct FndrFileInfo
*finder_info
;
769 finder_info
= (struct FndrFileInfo
*)&c_attr
.ca_finderinfo
[0];
770 flags
= SWAP_BE16(finder_info
->fdFlags
);
775 flags
= kIsInvisible
;
780 if (flags
& kIsInvisible
) {
788 /* Now that we have a record worth searching, see if it matches the search attributes */
789 if (rec
->recordType
== kHFSFileRecord
||
790 rec
->recordType
== kHFSPlusFileRecord
) {
791 if ((attrList
->fileattr
& ~ATTR_FILE_VALIDMASK
) != 0) { /* attr we do know about */
795 else if ((attrList
->fileattr
& ATTR_FILE_VALIDMASK
) != 0) {
796 searchAttributes
= attrList
->fileattr
;
798 /* File logical length (data fork) */
799 if ( searchAttributes
& ATTR_FILE_DATALENGTH
) {
800 matched
= CompareWideRange(
802 searchInfo1
->f
.dataLogicalLength
,
803 searchInfo2
->f
.dataLogicalLength
);
804 if (matched
== false) goto TestDone
;
808 /* File physical length (data fork) */
809 if ( searchAttributes
& ATTR_FILE_DATAALLOCSIZE
) {
810 matched
= CompareWideRange(
811 (u_int64_t
)datafork
.cf_blocks
* (u_int64_t
)vcb
->blockSize
,
812 searchInfo1
->f
.dataPhysicalLength
,
813 searchInfo2
->f
.dataPhysicalLength
);
814 if (matched
== false) goto TestDone
;
818 /* File logical length (resource fork) */
819 if ( searchAttributes
& ATTR_FILE_RSRCLENGTH
) {
820 matched
= CompareWideRange(
822 searchInfo1
->f
.resourceLogicalLength
,
823 searchInfo2
->f
.resourceLogicalLength
);
824 if (matched
== false) goto TestDone
;
828 /* File physical length (resource fork) */
829 if ( searchAttributes
& ATTR_FILE_RSRCALLOCSIZE
) {
830 matched
= CompareWideRange(
831 (u_int64_t
)rsrcfork
.cf_blocks
* (u_int64_t
)vcb
->blockSize
,
832 searchInfo1
->f
.resourcePhysicalLength
,
833 searchInfo2
->f
.resourcePhysicalLength
);
834 if (matched
== false) goto TestDone
;
839 atleastone
= true; /* to match SRCHFS_MATCHFILES */
843 * Check the directory attributes
845 else if (rec
->recordType
== kHFSFolderRecord
||
846 rec
->recordType
== kHFSPlusFolderRecord
) {
847 if ((attrList
->dirattr
& ~ATTR_DIR_VALIDMASK
) != 0) { /* attr we do know about */
851 else if ((attrList
->dirattr
& ATTR_DIR_VALIDMASK
) != 0) {
852 searchAttributes
= attrList
->dirattr
;
854 /* Directory valence */
855 if ( searchAttributes
& ATTR_DIR_ENTRYCOUNT
) {
856 matched
= CompareRange(c_attr
.ca_entries
,
857 searchInfo1
->d
.numFiles
,
858 searchInfo2
->d
.numFiles
);
859 if (matched
== false) goto TestDone
;
864 atleastone
= true; /* to match SRCHFS_MATCHDIRS */
869 * Check the common attributes
871 searchAttributes
= attrList
->commonattr
;
872 if ( (searchAttributes
& ATTR_CMN_VALIDMASK
) != 0 ) {
874 if ( searchAttributes
& ATTR_CMN_OBJID
) {
875 matched
= CompareRange(c_attr
.ca_fileid
,
877 searchInfo2
->nodeID
);
878 if (matched
== false) goto TestDone
;
883 if ( searchAttributes
& ATTR_CMN_PAROBJID
) {
884 HFSCatalogNodeID parentID
;
887 parentID
= key
->hfsPlus
.parentID
;
889 parentID
= key
->hfs
.parentID
;
891 matched
= CompareRange(parentID
, searchInfo1
->parentDirID
,
892 searchInfo2
->parentDirID
);
893 if (matched
== false) goto TestDone
;
897 /* Finder Info & Extended Finder Info where extFinderInfo is last 32 bytes */
898 if ( searchAttributes
& ATTR_CMN_FNDRINFO
) {
899 u_int32_t
*thisValue
;
900 thisValue
= (u_int32_t
*) &c_attr
.ca_finderinfo
;
903 * Note: ioFlFndrInfo and ioDrUsrWds have the same offset in search info, so
904 * no need to test the object type here.
906 matched
= CompareMasked(thisValue
,
907 (u_int32_t
*)&searchInfo1
->finderInfo
,
908 (u_int32_t
*) &searchInfo2
->finderInfo
, 8);
909 if (matched
== false) goto TestDone
;
914 if ( searchAttributes
& ATTR_CMN_CRTIME
) {
915 matched
= CompareRange(c_attr
.ca_itime
,
916 searchInfo1
->creationDate
.tv_sec
,
917 searchInfo2
->creationDate
.tv_sec
);
918 if (matched
== false) goto TestDone
;
923 if ( searchAttributes
& ATTR_CMN_MODTIME
) {
924 matched
= CompareRange(c_attr
.ca_mtime
,
925 searchInfo1
->modificationDate
.tv_sec
,
926 searchInfo2
->modificationDate
.tv_sec
);
927 if (matched
== false) goto TestDone
;
932 if ( searchAttributes
& ATTR_CMN_CHGTIME
) {
933 matched
= CompareRange(c_attr
.ca_ctime
,
934 searchInfo1
->changeDate
.tv_sec
,
935 searchInfo2
->changeDate
.tv_sec
);
936 if (matched
== false) goto TestDone
;
941 if ( searchAttributes
& ATTR_CMN_ACCTIME
) {
942 matched
= CompareRange(c_attr
.ca_atime
,
943 searchInfo1
->accessDate
.tv_sec
,
944 searchInfo2
->accessDate
.tv_sec
);
945 if (matched
== false) goto TestDone
;
950 if ( searchAttributes
& ATTR_CMN_BKUPTIME
) {
951 matched
= CompareRange(c_attr
.ca_btime
,
952 searchInfo1
->lastBackupDate
.tv_sec
,
953 searchInfo2
->lastBackupDate
.tv_sec
);
954 if (matched
== false) goto TestDone
;
959 if ( searchAttributes
& ATTR_CMN_OWNERID
) {
960 matched
= CompareRange(c_attr
.ca_uid
,
961 searchInfo1
->uid
, searchInfo2
->uid
);
962 if (matched
== false) goto TestDone
;
967 if ( searchAttributes
& ATTR_CMN_GRPID
) {
968 matched
= CompareRange(c_attr
.ca_gid
,
969 searchInfo1
->gid
, searchInfo2
->gid
);
970 if (matched
== false) goto TestDone
;
975 if ( searchAttributes
& ATTR_CMN_ACCESSMASK
) {
976 matched
= CompareRange((u_int32_t
)c_attr
.ca_mode
,
977 (u_int32_t
)searchInfo1
->mask
,
978 (u_int32_t
)searchInfo2
->mask
);
979 if (matched
== false) goto TestDone
;
984 /* If we got here w/o matching any, then set to false */
990 * Finally, determine whether we need to negate the sense of the match
991 * (i.e. find all objects that DON'T match).
993 if ( searchBits
& SRCHFS_NEGATEPARAMS
)
1001 * Adds another record to the packed array for output
1004 InsertMatch(struct hfsmount
*hfsmp
, uio_t a_uio
, CatalogRecord
*rec
,
1005 CatalogKey
*key
, struct attrlist
*returnAttrList
,
1006 void *attributesBuffer
, void *variableBuffer
, u_long
* nummatches
)
1009 void *rovingAttributesBuffer
;
1010 void *rovingVariableBuffer
;
1011 u_long packedBufferSize
;
1012 struct attrblock attrblk
;
1013 struct cat_desc c_desc
;
1014 struct cat_attr c_attr
;
1015 struct cat_fork datafork
;
1016 struct cat_fork rsrcfork
;
1018 bzero(&c_desc
, sizeof(c_desc
));
1019 bzero(&c_attr
, sizeof(c_attr
));
1020 rovingAttributesBuffer
= (char*)attributesBuffer
+ sizeof(u_long
); /* Reserve space for length field */
1021 rovingVariableBuffer
= variableBuffer
;
1023 /* Convert catalog record into cat_attr format. */
1024 cat_convertattr(hfsmp
, rec
, &c_attr
, &datafork
, &rsrcfork
);
1026 /* Hide our private meta data directories */
1027 if (c_attr
.ca_fileid
== hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
||
1028 c_attr
.ca_fileid
== hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
) {
1033 /* Hide the private journal files */
1035 ((c_attr
.ca_fileid
== hfsmp
->hfs_jnlfileid
) ||
1036 (c_attr
.ca_fileid
== hfsmp
->hfs_jnlinfoblkid
))) {
1041 if (returnAttrList
->commonattr
& ATTR_CMN_NAME
) {
1042 cat_convertkey(hfsmp
, key
, rec
, &c_desc
);
1044 c_desc
.cd_cnid
= c_attr
.ca_fileid
;
1045 if (hfsmp
->hfs_flags
& HFS_STANDARD
)
1046 c_desc
.cd_parentcnid
= key
->hfs
.parentID
;
1048 c_desc
.cd_parentcnid
= key
->hfsPlus
.parentID
;
1051 attrblk
.ab_attrlist
= returnAttrList
;
1052 attrblk
.ab_attrbufpp
= &rovingAttributesBuffer
;
1053 attrblk
.ab_varbufpp
= &rovingVariableBuffer
;
1054 attrblk
.ab_flags
= 0;
1055 attrblk
.ab_blocksize
= 0;
1056 attrblk
.ab_context
= vfs_context_current();
1058 hfs_packattrblk(&attrblk
, hfsmp
, NULL
, &c_desc
, &c_attr
, &datafork
, &rsrcfork
, current_proc());
1060 packedBufferSize
= (char*)rovingVariableBuffer
- (char*)attributesBuffer
;
1062 if ( packedBufferSize
> uio_resid(a_uio
) )
1063 return( errSearchBufferFull
);
1067 *((u_long
*)attributesBuffer
) = packedBufferSize
; /* Store length of fixed + var block */
1069 err
= uiomove( (caddr_t
)attributesBuffer
, packedBufferSize
, a_uio
); /* XXX should be packedBufferSize */
1071 cat_releasedesc(&c_desc
);
1078 UnpackSearchAttributeBlock( struct hfsmount
*hfsmp
, struct attrlist
*alist
, searchinfospec_t
*searchInfo
, void *attributeBuffer
)
1082 boolean_t is_64_bit
;
1084 DBG_ASSERT(searchInfo
!= NULL
);
1086 is_64_bit
= proc_is64bit(current_proc());
1088 bufferSize
= *((u_int32_t
*)attributeBuffer
);
1089 if (bufferSize
== 0)
1090 return (EINVAL
); /* XXX -DJB is a buffer size of zero ever valid for searchfs? */
1092 attributeBuffer
= (u_int32_t
*)attributeBuffer
+ 1; /* advance past the size */
1095 * UnPack common attributes
1097 a
= alist
->commonattr
;
1099 if ( a
& ATTR_CMN_NAME
) {
1103 s
= (char*) attributeBuffer
+ ((attrreference_t
*) attributeBuffer
)->attr_dataoffset
;
1104 len
= ((attrreference_t
*) attributeBuffer
)->attr_length
;
1106 if (len
> sizeof(searchInfo
->name
))
1109 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1110 /* Convert name to pascal string to match HFS B-Tree names */
1113 if (utf8_to_hfs(HFSTOVCB(hfsmp
), len
-1, (u_char
*)s
, (u_char
*)searchInfo
->name
) != 0)
1116 searchInfo
->nameLength
= searchInfo
->name
[0];
1118 searchInfo
->name
[0] = searchInfo
->nameLength
= 0;
1120 attributeBuffer
= (attrreference_t
*)attributeBuffer
+ 1;
1123 /* Convert name to Unicode to match HFS Plus B-Tree names */
1126 if (utf8_decodestr((u_int8_t
*)s
, len
-1, (UniChar
*)searchInfo
->name
, &ucslen
,
1127 sizeof(searchInfo
->name
), ':', UTF_DECOMPOSED
| UTF_ESCAPE_ILLEGAL
))
1130 searchInfo
->nameLength
= ucslen
/ sizeof(UniChar
);
1132 searchInfo
->nameLength
= 0;
1134 attributeBuffer
= (attrreference_t
*)attributeBuffer
+ 1;
1137 if ( a
& ATTR_CMN_OBJID
) {
1138 searchInfo
->nodeID
= ((fsobj_id_t
*) attributeBuffer
)->fid_objno
; /* ignore fid_generation */
1139 attributeBuffer
= (fsobj_id_t
*)attributeBuffer
+ 1;
1141 if ( a
& ATTR_CMN_PAROBJID
) {
1142 searchInfo
->parentDirID
= ((fsobj_id_t
*) attributeBuffer
)->fid_objno
; /* ignore fid_generation */
1143 attributeBuffer
= (fsobj_id_t
*)attributeBuffer
+ 1;
1146 if ( a
& ATTR_CMN_CRTIME
) {
1148 struct user_timespec tmp
;
1149 tmp
= *((struct user_timespec
*)attributeBuffer
);
1150 searchInfo
->creationDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1151 searchInfo
->creationDate
.tv_nsec
= tmp
.tv_nsec
;
1152 attributeBuffer
= (struct user_timespec
*)attributeBuffer
+ 1;
1155 searchInfo
->creationDate
= *((struct timespec
*)attributeBuffer
);
1156 attributeBuffer
= (struct timespec
*)attributeBuffer
+ 1;
1159 if ( a
& ATTR_CMN_MODTIME
) {
1161 struct user_timespec tmp
;
1162 tmp
= *((struct user_timespec
*)attributeBuffer
);
1163 searchInfo
->modificationDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1164 searchInfo
->modificationDate
.tv_nsec
= tmp
.tv_nsec
;
1165 attributeBuffer
= (struct user_timespec
*)attributeBuffer
+ 1;
1168 searchInfo
->modificationDate
= *((struct timespec
*)attributeBuffer
);
1169 attributeBuffer
= (struct timespec
*)attributeBuffer
+ 1;
1172 if ( a
& ATTR_CMN_CHGTIME
) {
1174 struct user_timespec tmp
;
1175 tmp
= *((struct user_timespec
*)attributeBuffer
);
1176 searchInfo
->changeDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1177 searchInfo
->changeDate
.tv_nsec
= tmp
.tv_nsec
;
1178 attributeBuffer
= (struct user_timespec
*)attributeBuffer
+ 1;
1181 searchInfo
->changeDate
= *((struct timespec
*)attributeBuffer
);
1182 attributeBuffer
= (struct timespec
*)attributeBuffer
+ 1;
1185 if ( a
& ATTR_CMN_ACCTIME
) {
1187 struct user_timespec tmp
;
1188 tmp
= *((struct user_timespec
*)attributeBuffer
);
1189 searchInfo
->accessDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1190 searchInfo
->accessDate
.tv_nsec
= tmp
.tv_nsec
;
1191 attributeBuffer
= (struct user_timespec
*)attributeBuffer
+ 1;
1194 searchInfo
->accessDate
= *((struct timespec
*)attributeBuffer
);
1195 attributeBuffer
= (struct timespec
*)attributeBuffer
+ 1;
1198 if ( a
& ATTR_CMN_BKUPTIME
) {
1200 struct user_timespec tmp
;
1201 tmp
= *((struct user_timespec
*)attributeBuffer
);
1202 searchInfo
->lastBackupDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1203 searchInfo
->lastBackupDate
.tv_nsec
= tmp
.tv_nsec
;
1204 attributeBuffer
= (struct user_timespec
*)attributeBuffer
+ 1;
1207 searchInfo
->lastBackupDate
= *((struct timespec
*)attributeBuffer
);
1208 attributeBuffer
= (struct timespec
*)attributeBuffer
+ 1;
1211 if ( a
& ATTR_CMN_FNDRINFO
) {
1212 bcopy( attributeBuffer
, searchInfo
->finderInfo
, sizeof(searchInfo
->finderInfo
) );
1213 attributeBuffer
= (u_int8_t
*)attributeBuffer
+ 32;
1215 if ( a
& ATTR_CMN_OWNERID
) {
1216 searchInfo
->uid
= *((uid_t
*)attributeBuffer
);
1217 attributeBuffer
= (uid_t
*)attributeBuffer
+ 1;
1219 if ( a
& ATTR_CMN_GRPID
) {
1220 searchInfo
->gid
= *((gid_t
*)attributeBuffer
);
1221 attributeBuffer
= (gid_t
*)attributeBuffer
+ 1;
1223 if ( a
& ATTR_CMN_ACCESSMASK
) {
1224 searchInfo
->mask
= *((mode_t
*)attributeBuffer
);
1225 attributeBuffer
= (mode_t
*)attributeBuffer
+ 1;
1227 if ( a
& ATTR_CMN_FILEID
) {
1228 searchInfo
->nodeID
= (u_int32_t
)*((u_int64_t
*) attributeBuffer
);
1229 attributeBuffer
= (u_int64_t
*)attributeBuffer
+ 1;
1231 if ( a
& ATTR_CMN_PARENTID
) {
1232 searchInfo
->parentDirID
= (u_int32_t
)*((u_int64_t
*) attributeBuffer
);
1233 attributeBuffer
= (u_int64_t
*)attributeBuffer
+ 1;
1239 if ( a
& ATTR_DIR_ENTRYCOUNT
) {
1240 searchInfo
->d
.numFiles
= *((u_int32_t
*)attributeBuffer
);
1241 attributeBuffer
= (u_int32_t
*)attributeBuffer
+ 1;
1245 a
= alist
->fileattr
;
1247 if ( a
& ATTR_FILE_DATALENGTH
) {
1248 searchInfo
->f
.dataLogicalLength
= *((off_t
*)attributeBuffer
);
1249 attributeBuffer
= (off_t
*)attributeBuffer
+ 1;
1251 if ( a
& ATTR_FILE_DATAALLOCSIZE
) {
1252 searchInfo
->f
.dataPhysicalLength
= *((off_t
*)attributeBuffer
);
1253 attributeBuffer
= (off_t
*)attributeBuffer
+ 1;
1255 if ( a
& ATTR_FILE_RSRCLENGTH
) {
1256 searchInfo
->f
.resourceLogicalLength
= *((off_t
*)attributeBuffer
);
1257 attributeBuffer
= (off_t
*)attributeBuffer
+ 1;
1259 if ( a
& ATTR_FILE_RSRCALLOCSIZE
) {
1260 searchInfo
->f
.resourcePhysicalLength
= *((off_t
*)attributeBuffer
);
1261 attributeBuffer
= (off_t
*)attributeBuffer
+ 1;