2 * Copyright (c) 1997-2004 Apple Computer, 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 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
37 #include <mach/machine/vm_types.h>
38 #include <sys/vnode.h>
39 #include <sys/malloc.h>
40 #include <sys/signalvar.h>
42 #include <sys/utfconv.h>
43 #include <sys/kauth.h>
44 #include <sys/vnode_internal.h>
48 #include "hfs_catalog.h"
49 #include "hfs_attrlist.h"
50 #include "hfs_endian.h"
52 #include "hfscommon/headers/FileMgrInternal.h"
53 #include "hfscommon/headers/HFSUnicodeWrappers.h"
54 #include "hfscommon/headers/BTreesPrivate.h"
55 #include "hfscommon/headers/BTreeScanner.h"
56 #include "hfscommon/headers/CatalogPrivate.h"
58 /* Search criterea. */
59 struct directoryInfoSpec
66 off_t dataLogicalLength
;
67 off_t dataPhysicalLength
;
68 off_t resourceLogicalLength
;
69 off_t resourcePhysicalLength
;
74 u_char name
[kHFSPlusMaxFileNameBytes
];
76 char attributes
; // see IM:Files 2-100
79 struct timespec creationDate
;
80 struct timespec modificationDate
;
81 struct timespec changeDate
;
82 struct timespec accessDate
;
83 struct timespec lastBackupDate
;
84 uint8_t finderInfo
[32];
88 struct fileInfoSpec f
;
89 struct directoryInfoSpec d
;
91 typedef struct searchinfospec searchinfospec_t
;
93 static void ResolveHardlink(ExtendedVCB
*vcb
, HFSPlusCatalogFile
*recp
);
96 static int UnpackSearchAttributeBlock(struct hfsmount
*hfsmp
, struct attrlist
*alist
,
97 searchinfospec_t
*searchInfo
, void *attributeBuffer
);
99 static int CheckCriteria( ExtendedVCB
*vcb
,
101 struct attrlist
*attrList
,
104 searchinfospec_t
*searchInfo1
,
105 searchinfospec_t
*searchInfo2
,
106 Boolean lookForDup
);
108 static int CheckAccess(ExtendedVCB
*vcb
, u_long searchBits
, CatalogKey
*key
, struct vfs_context
*ctx
);
110 static int InsertMatch(struct hfsmount
*hfsmp
, uio_t a_uio
, CatalogRecord
*rec
,
111 CatalogKey
*key
, struct attrlist
*returnAttrList
,
112 void *attributesBuffer
, void *variableBuffer
,
113 u_long
* nummatches
);
115 static Boolean
CompareRange(u_long val
, u_long low
, u_long high
);
116 static Boolean
CompareWideRange(u_int64_t val
, u_int64_t low
, u_int64_t high
);
118 static Boolean
CompareRange( u_long val
, u_long low
, u_long high
)
120 return( (val
>= low
) && (val
<= high
) );
123 static Boolean
CompareWideRange( u_int64_t val
, u_int64_t low
, u_int64_t high
)
125 return( (val
>= low
) && (val
<= high
) );
127 //#define CompareRange(val, low, high) ((val >= low) && (val <= high))
129 #if 1 // Installer workaround (2940423)
130 static Boolean
IsTargetName( searchinfospec_t
* searchInfoPtr
, Boolean isHFSPlus
);
131 #endif // Installer workaround
133 __private_extern__
int hfs_vnop_search(struct vnop_searchfs_args
*ap
);
136 /************************************************************************/
137 /* Entry for searchfs() */
138 /************************************************************************/
140 #define errSearchBufferFull 101 /* Internal search errors */
149 IN kauth_cred_t cred;
157 struct vnop_searchfs_args
*ap
; /*
158 struct vnodeop_desc *a_desc;
160 void *a_searchparams1;
161 void *a_searchparams2;
162 struct attrlist *a_searchattrs;
164 struct timeval *a_timelimit;
165 struct attrlist *a_returnattrs;
166 u_long *a_nummatches;
170 struct searchstate *a_searchstate;
171 vfs_context_t a_context;
174 ExtendedVCB
*vcb
= VTOVCB(ap
->a_vp
);
175 struct hfsmount
*hfsmp
;
177 searchinfospec_t searchInfo1
;
178 searchinfospec_t searchInfo2
;
179 void *attributesBuffer
;
180 void *variableBuffer
;
181 u_long fixedBlockSize
;
182 u_long eachReturnBufferSize
;
183 struct proc
*p
= proc_self();
186 int timerExpired
= false;
187 int doQuickExit
= false;
188 CatalogKey
* myCurrentKeyPtr
;
189 CatalogRecord
* myCurrentDataPtr
;
190 CatPosition
* myCatPositionPtr
;
191 BTScanState myBTScanState
;
192 user_addr_t user_start
= 0;
193 user_size_t user_len
= 0;
197 /* XXX Parameter check a_searchattrs? */
199 *(ap
->a_nummatches
) = 0;
201 if (ap
->a_options
& ~SRCHFS_VALIDOPTIONSMASK
)
204 /* SRCHFS_SKIPLINKS requires root access.
205 * This option cannot be used with either
206 * the ATTR_CMN_NAME or ATTR_CMN_PAROBJID
209 if (ap
->a_options
& SRCHFS_SKIPLINKS
) {
212 attrs
= ap
->a_searchattrs
->commonattr
| ap
->a_returnattrs
->commonattr
;
213 if (attrs
& (ATTR_CMN_NAME
| ATTR_CMN_PAROBJID
))
215 if ((err
= vfs_context_suser(ap
->a_context
)))
219 if (uio_resid(ap
->a_uio
) <= 0)
222 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
223 hfsmp
= VTOHFS(ap
->a_vp
);
225 searchTime
= kMaxMicroSecsInKernel
;
226 if (ap
->a_timelimit
->tv_sec
== 0 &&
227 ap
->a_timelimit
->tv_usec
> 0 &&
228 ap
->a_timelimit
->tv_usec
< kMaxMicroSecsInKernel
) {
229 searchTime
= ap
->a_timelimit
->tv_usec
;
232 /* UnPack the search boundries, searchInfo1, searchInfo2 */
233 err
= UnpackSearchAttributeBlock(hfsmp
, ap
->a_searchattrs
,
234 &searchInfo1
, ap
->a_searchparams1
);
236 err
= UnpackSearchAttributeBlock(hfsmp
, ap
->a_searchattrs
,
237 &searchInfo2
, ap
->a_searchparams2
);
240 fixedBlockSize
= sizeof(uint32_t) + hfs_attrblksize(ap
->a_returnattrs
); /* uint32_t for length word */
242 eachReturnBufferSize
= fixedBlockSize
;
244 if ( ap
->a_returnattrs
->commonattr
& ATTR_CMN_NAME
) /* XXX should be more robust! */
245 eachReturnBufferSize
+= kHFSPlusMaxFileNameBytes
+ 1;
247 MALLOC( attributesBuffer
, void *, eachReturnBufferSize
, M_TEMP
, M_WAITOK
);
248 variableBuffer
= (void*)((char*) attributesBuffer
+ fixedBlockSize
);
250 // XXXdbg - have to lock the user's buffer so we don't fault
251 // while holding the shared catalog file lock. see the comment
252 // in hfs_readdir() for more details.
254 if (hfsmp
->jnl
&& uio_isuserspace(ap
->a_uio
)) {
255 user_start
= uio_curriovbase(ap
->a_uio
);
256 user_len
= uio_curriovlen(ap
->a_uio
);
258 if ((err
= vslock(user_start
, user_len
)) != 0) {
260 goto ExitThisRoutine
;
264 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
266 catalogFCB
= GetFileControlBlock(vcb
->catalogRefNum
);
267 myCurrentKeyPtr
= NULL
;
268 myCurrentDataPtr
= NULL
;
269 myCatPositionPtr
= (CatPosition
*)ap
->a_searchstate
;
271 if (ap
->a_options
& SRCHFS_START
) {
272 /* Starting a new search. */
273 /* Make sure the on-disk Catalog file is current */
274 (void) hfs_fsync(vcb
->catalogRefNum
, MNT_WAIT
, 0, p
);
276 hfs_systemfile_unlock(hfsmp
, lockflags
);
277 journal_flush(hfsmp
->jnl
);
278 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
281 ap
->a_options
&= ~SRCHFS_START
;
282 bzero( (caddr_t
)myCatPositionPtr
, sizeof( *myCatPositionPtr
) );
283 err
= BTScanInitialize(catalogFCB
, 0, 0, 0, kCatSearchBufferSize
, &myBTScanState
);
285 #if 1 // Installer workaround (2940423)
286 // hack to get around installer problems when the installer expects search results
287 // to be in key order. At this point the problem appears to be limited to
288 // searches for "Library". The idea here is to go get the "Library" at root
289 // and return it first to the caller then continue the search as normal with
290 // the exception of taking care not to return a duplicate hit (see CheckCriteria)
291 if ( err
== E_NONE
&&
292 (ap
->a_searchattrs
->commonattr
& ATTR_CMN_NAME
) != 0 &&
293 IsTargetName( &searchInfo1
, isHFSPlus
) )
296 BTreeIterator iterator
;
297 FSBufferDescriptor btrec
;
302 bzero( (caddr_t
)&iterator
, sizeof( iterator
) );
303 keyp
= (CatalogKey
*) &iterator
.key
;
304 (void) BuildCatalogKeyUTF8(vcb
, kRootDirID
, "Library", kUndefinedStrLen
, keyp
, NULL
);
306 btrec
.bufferAddress
= &rec
;
308 btrec
.itemSize
= sizeof( rec
);
310 result
= BTSearchRecord( catalogFCB
, &iterator
, &btrec
, &reclen
, &iterator
);
311 if ( result
== E_NONE
) {
312 // need to unlock since CheckAccess assumes no lock held
313 hfs_systemfile_unlock(hfsmp
, lockflags
);
314 if (CheckCriteria(vcb
, ap
->a_options
, ap
->a_searchattrs
, &rec
,
315 keyp
, &searchInfo1
, &searchInfo2
, false) &&
316 CheckAccess(vcb
, ap
->a_options
, keyp
, ap
->a_context
)) {
318 result
= InsertMatch(hfsmp
, ap
->a_uio
, &rec
,
319 keyp
, ap
->a_returnattrs
,
320 attributesBuffer
, variableBuffer
,
322 if (result
== E_NONE
&& *(ap
->a_nummatches
) >= ap
->a_maxmatches
)
325 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
328 #endif // Installer workaround
330 /* Resuming a search. */
331 err
= BTScanInitialize(catalogFCB
, myCatPositionPtr
->nextNode
,
332 myCatPositionPtr
->nextRecord
,
333 myCatPositionPtr
->recordsFound
,
334 kCatSearchBufferSize
,
336 /* Make sure Catalog hasn't changed. */
338 && myCatPositionPtr
->writeCount
!= myBTScanState
.btcb
->writeCount
) {
339 myCatPositionPtr
->writeCount
= myBTScanState
.btcb
->writeCount
;
340 err
= EBUSY
; /* catChangedErr */
343 hfs_systemfile_unlock(hfsmp
, lockflags
);
346 goto ExitThisRoutine
;
347 #if 1 // Installer workaround (2940423)
350 #endif // Installer workaround
353 * Check all the catalog btree records...
354 * return the attributes for matching items
357 struct timeval myCurrentTime
;
358 struct timeval myElapsedTime
;
360 err
= BTScanNextRecord(&myBTScanState
, timerExpired
,
361 (void **)&myCurrentKeyPtr
, (void **)&myCurrentDataPtr
,
366 /* Resolve any hardlinks */
367 if (isHFSPlus
&& (ap
->a_options
& SRCHFS_SKIPLINKS
) == 0) {
368 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
369 ResolveHardlink(vcb
, (HFSPlusCatalogFile
*) myCurrentDataPtr
);
370 hfs_systemfile_unlock(hfsmp
, lockflags
);
372 if (CheckCriteria( vcb
, ap
->a_options
, ap
->a_searchattrs
, myCurrentDataPtr
,
373 myCurrentKeyPtr
, &searchInfo1
, &searchInfo2
, true )
374 && CheckAccess(vcb
, ap
->a_options
, myCurrentKeyPtr
, ap
->a_context
)) {
375 err
= InsertMatch(hfsmp
, ap
->a_uio
, myCurrentDataPtr
,
376 myCurrentKeyPtr
, ap
->a_returnattrs
,
377 attributesBuffer
, variableBuffer
, ap
->a_nummatches
);
380 * The last match didn't fit so come back
381 * to this record on the next trip.
383 --myBTScanState
.recordsFound
;
384 --myBTScanState
.recordNum
;
388 if (*(ap
->a_nummatches
) >= ap
->a_maxmatches
)
393 * Check our elapsed time and bail if we've hit the max.
394 * The idea here is to throttle the amount of time we
395 * spend in the kernel.
397 microuptime(&myCurrentTime
);
398 timersub(&myCurrentTime
, &myBTScanState
.startTime
, &myElapsedTime
);
399 /* Note: assumes kMaxMicroSecsInKernel is less than 1,000,000 */
400 if (myElapsedTime
.tv_sec
> 0
401 || myElapsedTime
.tv_usec
>= searchTime
) {
407 /* Update catalog position */
408 myCatPositionPtr
->writeCount
= myBTScanState
.btcb
->writeCount
;
410 BTScanTerminate(&myBTScanState
, &myCatPositionPtr
->nextNode
,
411 &myCatPositionPtr
->nextRecord
,
412 &myCatPositionPtr
->recordsFound
);
414 if ( err
== E_NONE
) {
415 err
= EAGAIN
; /* signal to the user to call searchfs again */
416 } else if ( err
== errSearchBufferFull
) {
417 if ( *(ap
->a_nummatches
) > 0 )
421 } else if ( err
== btNotFound
) {
422 err
= E_NONE
; /* the entire disk has been searched */
423 } else if ( err
== fsBTTimeOutErr
) {
428 FREE( attributesBuffer
, M_TEMP
);
430 if (hfsmp
->jnl
&& user_start
) {
431 vsunlock(user_start
, user_len
, TRUE
);
434 return (MacToVFSError(err
));
439 ResolveHardlink(ExtendedVCB
*vcb
, HFSPlusCatalogFile
*recp
)
441 if ((recp
->recordType
== kHFSPlusFileRecord
)
442 && (SWAP_BE32(recp
->userInfo
.fdType
) == kHardLinkFileType
)
443 && (SWAP_BE32(recp
->userInfo
.fdCreator
) == kHFSPlusCreator
)
444 && ((to_bsd_time(recp
->createDate
) == vcb
->vcbCrDate
) ||
445 (to_bsd_time(recp
->createDate
) == VCBTOHFS(vcb
)->hfs_metadata_createdate
))) {
448 /* Export link's cnid (a unique value) instead of inode's cnid */
449 saved_cnid
= recp
->fileID
;
450 (void) resolvelink(VCBTOHFS(vcb
), recp
->bsdInfo
.special
.iNodeNum
, recp
);
451 recp
->fileID
= saved_cnid
;
457 CompareMasked(const UInt32
*thisValue
, const UInt32
*compareData
,
458 const UInt32
*compareMask
, UInt32 count
)
463 matched
= true; /* Assume it will all match */
465 for (i
=0; i
<count
; i
++) {
466 if (((*thisValue
++ ^ *compareData
++) & *compareMask
++) != 0) {
477 ComparePartialUnicodeName (register ConstUniCharArrayPtr str
, register ItemCount s_len
,
478 register ConstUniCharArrayPtr find
, register ItemCount f_len
)
480 if (f_len
== 0 || s_len
== 0)
486 } while (FastUnicodeCompare(str
++, f_len
, find
, f_len
) != 0);
493 ComparePartialPascalName ( register ConstStr31Param str
, register ConstStr31Param find
)
495 register u_char s_len
= str
[0];
496 register u_char f_len
= find
[0];
497 register u_char
*tsp
;
500 if (f_len
== 0 || s_len
== 0)
503 bcopy(str
, tmpstr
, s_len
+ 1);
506 while (s_len
-- >= f_len
) {
509 if (FastRelString(tsp
++, find
) == 0)
518 // Determine if a name is "inappropriate" where the definition
519 // of "inappropriate" is up to higher level execs. Currently
520 // that's limited to /System.
523 is_inappropriate_name(char *name
, int len
)
525 const char *bad_names
[] = { "System" };
526 int bad_len
[] = { 6 };
529 for(i
=0; i
< (int) (sizeof(bad_names
) / sizeof(bad_names
[0])); i
++) {
530 if (len
== bad_len
[i
] && strcmp(name
, bad_names
[i
]) == 0) {
535 // if we get here, no name matched
542 * Check to see if caller has access rights to this item
546 CheckAccess(ExtendedVCB
*theVCBPtr
, u_long searchBits
, CatalogKey
*theKeyPtr
, struct vfs_context
*ctx
)
551 HFSCatalogNodeID myNodeID
;
553 struct FndrDirInfo
*finfop
;
554 struct vnode
* vp
= NULL
;
556 myResult
= 0; /* default to "no access" */
558 if (!vfs_context_suser(ctx
)) {
559 myResult
= 1; /* allow access */
560 goto ExitThisRoutine
; /* root always has access */
563 hfsmp
= VCBTOHFS( theVCBPtr
);
564 isHFSPlus
= ( theVCBPtr
->vcbSigWord
== kHFSPlusSigWord
);
566 myNodeID
= theKeyPtr
->hfsPlus
.parentID
;
568 myNodeID
= theKeyPtr
->hfs
.parentID
;
570 while ( myNodeID
>= kRootDirID
) {
573 /* now go get catalog data for this directory */
574 myErr
= hfs_vget(hfsmp
, myNodeID
, &vp
, 0);
576 goto ExitThisRoutine
; /* no access */
580 finfop
= (struct FndrDirInfo
*)&cp
->c_attr
.ca_finderinfo
[0];
582 if ( searchBits
& SRCHFS_SKIPPACKAGES
) {
583 if ( (SWAP_BE16(finfop
->frFlags
) & kHasBundle
)
584 || (cp
->c_desc
.cd_nameptr
!= NULL
585 && is_package_name(cp
->c_desc
.cd_nameptr
, cp
->c_desc
.cd_namelen
)) ) {
587 goto ExitThisRoutine
;
591 if ( searchBits
& SRCHFS_SKIPINAPPROPRIATE
) {
592 if ( cp
->c_parentcnid
== kRootDirID
&& cp
->c_desc
.cd_nameptr
!= NULL
&&
593 is_inappropriate_name(cp
->c_desc
.cd_nameptr
, cp
->c_desc
.cd_namelen
) ) {
595 goto ExitThisRoutine
;
599 if ( (searchBits
& SRCHFS_SKIPINVISIBLE
) &&
600 (SWAP_BE16(finfop
->frFlags
) & kIsInvisible
) ) {
602 goto ExitThisRoutine
;
605 myNodeID
= cp
->c_parentcnid
; /* move up the hierarchy */
606 hfs_unlock(VTOC(vp
));
607 if (vp
->v_type
== VDIR
) {
608 myErr
= vnode_authorize(vp
, NULL
, (KAUTH_VNODE_SEARCH
| KAUTH_VNODE_LIST_DIRECTORY
), ctx
);
610 myErr
= vnode_authorize(vp
, NULL
, (KAUTH_VNODE_SEARCH
), ctx
);
615 goto ExitThisRoutine
; /* no access */
618 myResult
= 1; /* allow access */
622 hfs_unlock(VTOC(vp
));
630 CheckCriteria( ExtendedVCB
*vcb
,
632 struct attrlist
*attrList
,
635 searchinfospec_t
*searchInfo1
,
636 searchinfospec_t
*searchInfo2
,
639 Boolean matched
, atleastone
;
641 attrgroup_t searchAttributes
;
642 struct cat_attr c_attr
;
643 struct cat_fork datafork
;
644 struct cat_fork rsrcfork
;
646 bzero(&c_attr
, sizeof(c_attr
));
647 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
649 switch (rec
->recordType
) {
650 case kHFSFolderRecord
:
651 case kHFSPlusFolderRecord
:
652 if ( (searchBits
& SRCHFS_MATCHDIRS
) == 0 ) { /* If we are NOT searching folders */
659 if ( (searchBits
& SRCHFS_MATCHFILES
) == 0 ) { /* If we are NOT searching files */
665 case kHFSPlusFileRecord
:
666 /* Check if hardlink links should be skipped. */
667 if (searchBits
& SRCHFS_SKIPLINKS
) {
668 cnid_t parid
= key
->hfsPlus
.parentID
;
669 HFSPlusCatalogFile
*filep
= (HFSPlusCatalogFile
*)rec
;
671 if ((SWAP_BE32(filep
->userInfo
.fdType
) == kHardLinkFileType
) &&
672 (SWAP_BE32(filep
->userInfo
.fdCreator
) == kHFSPlusCreator
)) {
673 return (false); /* skip over link records */
674 } else if ((parid
== VCBTOHFS(vcb
)->hfs_privdir_desc
.cd_cnid
) &&
675 (filep
->bsdInfo
.special
.linkCount
== 0)) {
676 return (false); /* skip over unlinked files */
678 } else if (key
->hfsPlus
.parentID
== VCBTOHFS(vcb
)->hfs_privdir_desc
.cd_cnid
) {
679 return (false); /* skip over private files */
682 if ( (searchBits
& SRCHFS_MATCHFILES
) == 0 ) { /* If we are NOT searching files */
688 default: /* Never match a thread record or any other type. */
689 return( false ); /* Not a file or folder record, so can't search it */
692 matched
= true; /* Assume we got a match */
693 atleastone
= false; /* Dont insert unless we match at least one criteria */
695 /* First, attempt to match the name -- either partial or complete */
696 if ( attrList
->commonattr
& ATTR_CMN_NAME
) {
698 /* Check for partial/full HFS Plus name match */
700 if ( searchBits
& SRCHFS_MATCHPARTIALNAMES
) {
701 matched
= ComparePartialUnicodeName(key
->hfsPlus
.nodeName
.unicode
,
702 key
->hfsPlus
.nodeName
.length
,
703 (UniChar
*)searchInfo1
->name
,
704 searchInfo1
->nameLength
);
705 } else /* full HFS Plus name match */ {
706 matched
= (FastUnicodeCompare(key
->hfsPlus
.nodeName
.unicode
,
707 key
->hfsPlus
.nodeName
.length
,
708 (UniChar
*)searchInfo1
->name
,
709 searchInfo1
->nameLength
) == 0);
712 /* Check for partial/full HFS name match */
714 if ( searchBits
& SRCHFS_MATCHPARTIALNAMES
)
715 matched
= ComparePartialPascalName(key
->hfs
.nodeName
, (u_char
*)searchInfo1
->name
);
716 else /* full HFS name match */
717 matched
= (FastRelString(key
->hfs
.nodeName
, (u_char
*)searchInfo1
->name
) == 0);
720 #if 1 // Installer workaround (2940423)
722 HFSCatalogNodeID parentID
;
724 parentID
= key
->hfsPlus
.parentID
;
726 parentID
= key
->hfs
.parentID
;
728 if ( matched
&& parentID
== kRootDirID
&&
729 IsTargetName( searchInfo1
, isHFSPlus
) )
732 #endif // Installer workaround
734 if ( matched
== false || (searchBits
& ~SRCHFS_MATCHPARTIALNAMES
) == 0 )
735 goto TestDone
; /* no match, or nothing more to compare */
740 /* Convert catalog record into cat_attr format. */
741 cat_convertattr(VCBTOHFS(vcb
), rec
, &c_attr
, &datafork
, &rsrcfork
);
743 if (searchBits
& SRCHFS_SKIPINVISIBLE
) {
746 switch (rec
->recordType
) {
747 case kHFSFolderRecord
:
748 case kHFSPlusFolderRecord
: {
749 struct FndrDirInfo
*finder_info
;
751 finder_info
= (struct FndrDirInfo
*)&c_attr
.ca_finderinfo
[0];
752 flags
= SWAP_BE16(finder_info
->frFlags
);
757 case kHFSPlusFileRecord
: {
758 struct FndrFileInfo
*finder_info
;
760 finder_info
= (struct FndrFileInfo
*)&c_attr
.ca_finderinfo
[0];
761 flags
= SWAP_BE16(finder_info
->fdFlags
);
766 flags
= kIsInvisible
;
771 if (flags
& kIsInvisible
) {
779 /* Now that we have a record worth searching, see if it matches the search attributes */
780 if (rec
->recordType
== kHFSFileRecord
||
781 rec
->recordType
== kHFSPlusFileRecord
) {
782 if ((attrList
->fileattr
& ~ATTR_FILE_VALIDMASK
) != 0) { /* attr we do know about */
786 else if ((attrList
->fileattr
& ATTR_FILE_VALIDMASK
) != 0) {
787 searchAttributes
= attrList
->fileattr
;
789 /* File logical length (data fork) */
790 if ( searchAttributes
& ATTR_FILE_DATALENGTH
) {
791 matched
= CompareWideRange(
793 searchInfo1
->f
.dataLogicalLength
,
794 searchInfo2
->f
.dataLogicalLength
);
795 if (matched
== false) goto TestDone
;
799 /* File physical length (data fork) */
800 if ( searchAttributes
& ATTR_FILE_DATAALLOCSIZE
) {
801 matched
= CompareWideRange(
802 (u_int64_t
)datafork
.cf_blocks
* (u_int64_t
)vcb
->blockSize
,
803 searchInfo1
->f
.dataPhysicalLength
,
804 searchInfo2
->f
.dataPhysicalLength
);
805 if (matched
== false) goto TestDone
;
809 /* File logical length (resource fork) */
810 if ( searchAttributes
& ATTR_FILE_RSRCLENGTH
) {
811 matched
= CompareWideRange(
813 searchInfo1
->f
.resourceLogicalLength
,
814 searchInfo2
->f
.resourceLogicalLength
);
815 if (matched
== false) goto TestDone
;
819 /* File physical length (resource fork) */
820 if ( searchAttributes
& ATTR_FILE_RSRCALLOCSIZE
) {
821 matched
= CompareWideRange(
822 (u_int64_t
)rsrcfork
.cf_blocks
* (u_int64_t
)vcb
->blockSize
,
823 searchInfo1
->f
.resourcePhysicalLength
,
824 searchInfo2
->f
.resourcePhysicalLength
);
825 if (matched
== false) goto TestDone
;
830 atleastone
= true; /* to match SRCHFS_MATCHFILES */
834 * Check the directory attributes
836 else if (rec
->recordType
== kHFSFolderRecord
||
837 rec
->recordType
== kHFSPlusFolderRecord
) {
838 if ((attrList
->dirattr
& ~ATTR_DIR_VALIDMASK
) != 0) { /* attr we do know about */
842 else if ((attrList
->dirattr
& ATTR_DIR_VALIDMASK
) != 0) {
843 searchAttributes
= attrList
->dirattr
;
845 /* Directory valence */
846 if ( searchAttributes
& ATTR_DIR_ENTRYCOUNT
) {
847 matched
= CompareRange(c_attr
.ca_entries
,
848 searchInfo1
->d
.numFiles
,
849 searchInfo2
->d
.numFiles
);
850 if (matched
== false) goto TestDone
;
855 atleastone
= true; /* to match SRCHFS_MATCHDIRS */
860 * Check the common attributes
862 searchAttributes
= attrList
->commonattr
;
863 if ( (searchAttributes
& ATTR_CMN_VALIDMASK
) != 0 ) {
865 if ( searchAttributes
& ATTR_CMN_OBJID
) {
866 matched
= CompareRange(c_attr
.ca_fileid
,
868 searchInfo2
->nodeID
);
869 if (matched
== false) goto TestDone
;
874 if ( searchAttributes
& ATTR_CMN_PAROBJID
) {
875 HFSCatalogNodeID parentID
;
878 parentID
= key
->hfsPlus
.parentID
;
880 parentID
= key
->hfs
.parentID
;
882 matched
= CompareRange(parentID
, searchInfo1
->parentDirID
,
883 searchInfo2
->parentDirID
);
884 if (matched
== false) goto TestDone
;
888 /* Finder Info & Extended Finder Info where extFinderInfo is last 32 bytes */
889 if ( searchAttributes
& ATTR_CMN_FNDRINFO
) {
891 thisValue
= (UInt32
*) &c_attr
.ca_finderinfo
;
894 * Note: ioFlFndrInfo and ioDrUsrWds have the same offset in search info, so
895 * no need to test the object type here.
897 matched
= CompareMasked(thisValue
,
898 (UInt32
*)&searchInfo1
->finderInfo
,
899 (UInt32
*) &searchInfo2
->finderInfo
, 8);
900 if (matched
== false) goto TestDone
;
905 if ( searchAttributes
& ATTR_CMN_CRTIME
) {
906 matched
= CompareRange(c_attr
.ca_itime
,
907 searchInfo1
->creationDate
.tv_sec
,
908 searchInfo2
->creationDate
.tv_sec
);
909 if (matched
== false) goto TestDone
;
914 if ( searchAttributes
& ATTR_CMN_MODTIME
) {
915 matched
= CompareRange(c_attr
.ca_mtime
,
916 searchInfo1
->modificationDate
.tv_sec
,
917 searchInfo2
->modificationDate
.tv_sec
);
918 if (matched
== false) goto TestDone
;
923 if ( searchAttributes
& ATTR_CMN_CHGTIME
) {
924 matched
= CompareRange(c_attr
.ca_ctime
,
925 searchInfo1
->changeDate
.tv_sec
,
926 searchInfo2
->changeDate
.tv_sec
);
927 if (matched
== false) goto TestDone
;
932 if ( searchAttributes
& ATTR_CMN_ACCTIME
) {
933 matched
= CompareRange(c_attr
.ca_atime
,
934 searchInfo1
->accessDate
.tv_sec
,
935 searchInfo2
->accessDate
.tv_sec
);
936 if (matched
== false) goto TestDone
;
941 if ( searchAttributes
& ATTR_CMN_BKUPTIME
) {
942 matched
= CompareRange(c_attr
.ca_btime
,
943 searchInfo1
->lastBackupDate
.tv_sec
,
944 searchInfo2
->lastBackupDate
.tv_sec
);
945 if (matched
== false) goto TestDone
;
950 if ( searchAttributes
& ATTR_CMN_OWNERID
) {
951 matched
= CompareRange(c_attr
.ca_uid
,
952 searchInfo1
->uid
, searchInfo2
->uid
);
953 if (matched
== false) goto TestDone
;
958 if ( searchAttributes
& ATTR_CMN_GRPID
) {
959 matched
= CompareRange(c_attr
.ca_gid
,
960 searchInfo1
->gid
, searchInfo2
->gid
);
961 if (matched
== false) goto TestDone
;
966 if ( searchAttributes
& ATTR_CMN_ACCESSMASK
) {
967 matched
= CompareRange((uint32_t)c_attr
.ca_mode
,
968 (uint32_t)searchInfo1
->mask
,
969 (uint32_t)searchInfo2
->mask
);
970 if (matched
== false) goto TestDone
;
975 /* If we got here w/o matching any, then set to false */
981 * Finally, determine whether we need to negate the sense of the match
982 * (i.e. find all objects that DON'T match).
984 if ( searchBits
& SRCHFS_NEGATEPARAMS
)
992 * Adds another record to the packed array for output
995 InsertMatch(struct hfsmount
*hfsmp
, uio_t a_uio
, CatalogRecord
*rec
,
996 CatalogKey
*key
, struct attrlist
*returnAttrList
,
997 void *attributesBuffer
, void *variableBuffer
, u_long
* nummatches
)
1000 void *rovingAttributesBuffer
;
1001 void *rovingVariableBuffer
;
1002 u_long packedBufferSize
;
1003 u_long privateDir
= hfsmp
->hfs_privdir_desc
.cd_cnid
;
1004 struct attrblock attrblk
;
1005 struct cat_desc c_desc
;
1006 struct cat_attr c_attr
;
1007 struct cat_fork datafork
;
1008 struct cat_fork rsrcfork
;
1010 bzero(&c_desc
, sizeof(c_desc
));
1011 bzero(&c_attr
, sizeof(c_attr
));
1012 rovingAttributesBuffer
= (char*)attributesBuffer
+ sizeof(u_long
); /* Reserve space for length field */
1013 rovingVariableBuffer
= variableBuffer
;
1015 /* Convert catalog record into cat_attr format. */
1016 cat_convertattr(hfsmp
, rec
, &c_attr
, &datafork
, &rsrcfork
);
1018 /* hide our private meta data directory */
1019 if ((privateDir
!= 0) && (c_attr
.ca_fileid
== privateDir
)) {
1024 /* Hide the private journal files */
1026 ((c_attr
.ca_fileid
== hfsmp
->hfs_jnlfileid
) ||
1027 (c_attr
.ca_fileid
== hfsmp
->hfs_jnlinfoblkid
))) {
1032 if (returnAttrList
->commonattr
& ATTR_CMN_NAME
) {
1033 cat_convertkey(hfsmp
, key
, rec
, &c_desc
);
1035 c_desc
.cd_cnid
= c_attr
.ca_fileid
;
1036 if (hfsmp
->hfs_flags
& HFS_STANDARD
)
1037 c_desc
.cd_parentcnid
= key
->hfs
.parentID
;
1039 c_desc
.cd_parentcnid
= key
->hfsPlus
.parentID
;
1042 attrblk
.ab_attrlist
= returnAttrList
;
1043 attrblk
.ab_attrbufpp
= &rovingAttributesBuffer
;
1044 attrblk
.ab_varbufpp
= &rovingVariableBuffer
;
1045 attrblk
.ab_flags
= 0;
1046 attrblk
.ab_blocksize
= 0;
1048 hfs_packattrblk(&attrblk
, hfsmp
, NULL
, &c_desc
, &c_attr
, &datafork
, &rsrcfork
, current_proc());
1050 packedBufferSize
= (char*)rovingVariableBuffer
- (char*)attributesBuffer
;
1052 if ( packedBufferSize
> uio_resid(a_uio
) )
1053 return( errSearchBufferFull
);
1057 *((u_long
*)attributesBuffer
) = packedBufferSize
; /* Store length of fixed + var block */
1059 err
= uiomove( (caddr_t
)attributesBuffer
, packedBufferSize
, a_uio
); /* XXX should be packedBufferSize */
1061 cat_releasedesc(&c_desc
);
1068 UnpackSearchAttributeBlock( struct hfsmount
*hfsmp
, struct attrlist
*alist
, searchinfospec_t
*searchInfo
, void *attributeBuffer
)
1072 boolean_t is_64_bit
;
1074 DBG_ASSERT(searchInfo
!= NULL
);
1076 is_64_bit
= proc_is64bit(current_proc());
1078 bufferSize
= *((uint32_t *)attributeBuffer
);
1079 if (bufferSize
== 0)
1080 return (EINVAL
); /* XXX -DJB is a buffer size of zero ever valid for searchfs? */
1082 ++((uint32_t *)attributeBuffer
); /* advance past the size */
1085 * UnPack common attributes
1087 a
= alist
->commonattr
;
1089 if ( a
& ATTR_CMN_NAME
) {
1093 s
= (char*) attributeBuffer
+ ((attrreference_t
*) attributeBuffer
)->attr_dataoffset
;
1094 len
= ((attrreference_t
*) attributeBuffer
)->attr_length
;
1096 if (len
> sizeof(searchInfo
->name
))
1099 if (hfsmp
->hfs_flags
& HFS_STANDARD
) {
1100 /* Convert name to pascal string to match HFS B-Tree names */
1103 if (utf8_to_hfs(HFSTOVCB(hfsmp
), len
-1, s
, (u_char
*)searchInfo
->name
) != 0)
1106 searchInfo
->nameLength
= searchInfo
->name
[0];
1108 searchInfo
->name
[0] = searchInfo
->nameLength
= 0;
1110 ++((attrreference_t
*)attributeBuffer
);
1113 /* Convert name to Unicode to match HFS Plus B-Tree names */
1116 if (utf8_decodestr(s
, len
-1, (UniChar
*)searchInfo
->name
, &ucslen
,
1117 sizeof(searchInfo
->name
), ':', UTF_DECOMPOSED
))
1120 searchInfo
->nameLength
= ucslen
/ sizeof(UniChar
);
1122 searchInfo
->nameLength
= 0;
1124 ++((attrreference_t
*)attributeBuffer
);
1127 if ( a
& ATTR_CMN_OBJID
) {
1128 searchInfo
->nodeID
= ((fsobj_id_t
*) attributeBuffer
)->fid_objno
; /* ignore fid_generation */
1129 ++((fsobj_id_t
*)attributeBuffer
);
1131 if ( a
& ATTR_CMN_PAROBJID
) {
1132 searchInfo
->parentDirID
= ((fsobj_id_t
*) attributeBuffer
)->fid_objno
; /* ignore fid_generation */
1133 ++((fsobj_id_t
*)attributeBuffer
);
1135 if ( a
& ATTR_CMN_CRTIME
) {
1137 struct user_timespec tmp
;
1138 tmp
= *((struct user_timespec
*)attributeBuffer
);
1139 searchInfo
->creationDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1140 searchInfo
->creationDate
.tv_nsec
= tmp
.tv_nsec
;
1141 ++((struct user_timespec
*)attributeBuffer
);
1144 searchInfo
->creationDate
= *((struct timespec
*)attributeBuffer
);
1145 ++((struct timespec
*)attributeBuffer
);
1148 if ( a
& ATTR_CMN_MODTIME
) {
1150 struct user_timespec tmp
;
1151 tmp
= *((struct user_timespec
*)attributeBuffer
);
1152 searchInfo
->modificationDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1153 searchInfo
->modificationDate
.tv_nsec
= tmp
.tv_nsec
;
1154 ++((struct user_timespec
*)attributeBuffer
);
1157 searchInfo
->modificationDate
= *((struct timespec
*)attributeBuffer
);
1158 ++((struct timespec
*)attributeBuffer
);
1161 if ( a
& ATTR_CMN_CHGTIME
) {
1163 struct user_timespec tmp
;
1164 tmp
= *((struct user_timespec
*)attributeBuffer
);
1165 searchInfo
->changeDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1166 searchInfo
->changeDate
.tv_nsec
= tmp
.tv_nsec
;
1167 ++((struct user_timespec
*)attributeBuffer
);
1170 searchInfo
->changeDate
= *((struct timespec
*)attributeBuffer
);
1171 ++((struct timespec
*)attributeBuffer
);
1174 if ( a
& ATTR_CMN_ACCTIME
) {
1176 struct user_timespec tmp
;
1177 tmp
= *((struct user_timespec
*)attributeBuffer
);
1178 searchInfo
->accessDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1179 searchInfo
->accessDate
.tv_nsec
= tmp
.tv_nsec
;
1180 ++((struct user_timespec
*)attributeBuffer
);
1183 searchInfo
->accessDate
= *((struct timespec
*)attributeBuffer
);
1184 ++((struct timespec
*)attributeBuffer
);
1187 if ( a
& ATTR_CMN_BKUPTIME
) {
1189 struct user_timespec tmp
;
1190 tmp
= *((struct user_timespec
*)attributeBuffer
);
1191 searchInfo
->lastBackupDate
.tv_sec
= (time_t)tmp
.tv_sec
;
1192 searchInfo
->lastBackupDate
.tv_nsec
= tmp
.tv_nsec
;
1193 ++((struct user_timespec
*)attributeBuffer
);
1196 searchInfo
->lastBackupDate
= *((struct timespec
*)attributeBuffer
);
1197 ++((struct timespec
*)attributeBuffer
);
1200 if ( a
& ATTR_CMN_FNDRINFO
) {
1201 bcopy( attributeBuffer
, searchInfo
->finderInfo
, sizeof(searchInfo
->finderInfo
) );
1202 (uint8_t *)attributeBuffer
+= 32;
1204 if ( a
& ATTR_CMN_OWNERID
) {
1205 searchInfo
->uid
= *((uid_t
*)attributeBuffer
);
1206 ++((uid_t
*)attributeBuffer
);
1208 if ( a
& ATTR_CMN_GRPID
) {
1209 searchInfo
->gid
= *((gid_t
*)attributeBuffer
);
1210 ++((gid_t
*)attributeBuffer
);
1212 if ( a
& ATTR_CMN_ACCESSMASK
) {
1213 searchInfo
->mask
= *((mode_t
*)attributeBuffer
);
1214 ++((mode_t
*)attributeBuffer
);
1220 if ( a
& ATTR_DIR_ENTRYCOUNT
) {
1221 searchInfo
->d
.numFiles
= *((u_int32_t
*)attributeBuffer
);
1222 ++((u_int32_t
*)attributeBuffer
);
1226 a
= alist
->fileattr
;
1228 if ( a
& ATTR_FILE_DATALENGTH
) {
1229 searchInfo
->f
.dataLogicalLength
= *((off_t
*)attributeBuffer
);
1230 ++((off_t
*)attributeBuffer
);
1232 if ( a
& ATTR_FILE_DATAALLOCSIZE
) {
1233 searchInfo
->f
.dataPhysicalLength
= *((off_t
*)attributeBuffer
);
1234 ++((off_t
*)attributeBuffer
);
1236 if ( a
& ATTR_FILE_RSRCLENGTH
) {
1237 searchInfo
->f
.resourceLogicalLength
= *((off_t
*)attributeBuffer
);
1238 ++((off_t
*)attributeBuffer
);
1240 if ( a
& ATTR_FILE_RSRCALLOCSIZE
) {
1241 searchInfo
->f
.resourcePhysicalLength
= *((off_t
*)attributeBuffer
);
1242 ++((off_t
*)attributeBuffer
);
1250 #if 1 // Installer workaround (2940423)
1251 /* this routine was added as part of the work around where some installers would fail */
1252 /* because they incorrectly assumed search results were in some kind of order. */
1253 /* This routine is used to indentify the problematic target. At this point we */
1254 /* only know of one. This routine could be modified for more (I hope not). */
1255 static Boolean
IsTargetName( searchinfospec_t
* searchInfoPtr
, Boolean isHFSPlus
)
1257 if ( searchInfoPtr
->name
== NULL
)
1261 HFSUniStr255 myName
= {
1262 7, /* number of unicode characters */
1264 'L','i','b','r','a','r','y'
1267 if ( FastUnicodeCompare( myName
.unicode
, myName
.length
,
1268 (UniChar
*)searchInfoPtr
->name
,
1269 searchInfoPtr
->nameLength
) == 0 ) {
1274 u_char myName
[32] = {
1275 0x07,'L','i','b','r','a','r','y'
1277 if ( FastRelString(myName
, (u_char
*)searchInfoPtr
->name
) == 0 ) {
1283 } /* IsTargetName */
1284 #endif // Installer workaround