2 * Copyright (c) 1997-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/kernel.h>
35 #include <mach/machine/vm_types.h>
36 #include <sys/vnode.h>
37 #include <sys/malloc.h>
38 #include <sys/signalvar.h>
40 #include <sys/utfconv.h>
44 #include "hfs_catalog.h"
45 #include "hfs_attrlist.h"
46 #include "hfs_endian.h"
48 #include "hfscommon/headers/FileMgrInternal.h"
49 #include "hfscommon/headers/CatalogPrivate.h"
50 #include "hfscommon/headers/HFSUnicodeWrappers.h"
51 #include "hfscommon/headers/BTreesPrivate.h"
52 #include "hfscommon/headers/BTreeScanner.h"
55 /* Search criterea. */
56 struct directoryInfoSpec
63 off_t dataLogicalLength
;
64 off_t dataPhysicalLength
;
65 off_t resourceLogicalLength
;
66 off_t resourcePhysicalLength
;
71 u_char name
[kHFSPlusMaxFileNameBytes
];
73 char attributes
; // see IM:Files 2-100
76 struct timespec creationDate
;
77 struct timespec modificationDate
;
78 struct timespec changeDate
;
79 struct timespec accessDate
;
80 struct timespec lastBackupDate
;
85 struct fileInfoSpec f
;
86 struct directoryInfoSpec d
;
88 typedef struct searchinfospec searchinfospec_t
;
90 static void ResolveHardlink(ExtendedVCB
*vcb
, HFSPlusCatalogFile
*recp
);
93 static int UnpackSearchAttributeBlock(struct vnode
*vp
, struct attrlist
*alist
,
94 searchinfospec_t
*searchInfo
, void *attributeBuffer
);
96 static int CheckCriteria( ExtendedVCB
*vcb
,
98 struct attrlist
*attrList
,
101 searchinfospec_t
*searchInfo1
,
102 searchinfospec_t
*searchInfo2
,
103 Boolean lookForDup
);
105 static int CheckAccess(ExtendedVCB
*vcb
, CatalogKey
*key
, struct proc
*p
);
107 static int InsertMatch(struct vnode
*vp
, struct uio
*a_uio
, CatalogRecord
*rec
,
108 CatalogKey
*key
, struct attrlist
*returnAttrList
,
109 void *attributesBuffer
, void *variableBuffer
,
110 u_long bufferSize
, u_long
* nummatches
);
112 static Boolean
CompareRange(u_long val
, u_long low
, u_long high
);
113 static Boolean
CompareWideRange(u_int64_t val
, u_int64_t low
, u_int64_t high
);
115 static Boolean
CompareRange( u_long val
, u_long low
, u_long high
)
117 return( (val
>= low
) && (val
<= high
) );
120 static Boolean
CompareWideRange( u_int64_t val
, u_int64_t low
, u_int64_t high
)
122 return( (val
>= low
) && (val
<= high
) );
124 //#define CompareRange(val, low, high) ((val >= low) && (val <= high))
126 #if 1 // Installer workaround (2940423)
127 static Boolean
IsTargetName( searchinfospec_t
* searchInfoPtr
, Boolean isHFSPlus
);
128 #endif // Installer workaround
130 extern int cat_convertkey(
131 struct hfsmount
*hfsmp
,
133 CatalogRecord
* recp
,
134 struct cat_desc
*descp
);
136 extern void cat_convertattr(
137 struct hfsmount
*hfsmp
,
138 CatalogRecord
* recp
,
139 struct cat_attr
*attrp
,
140 struct cat_fork
*datafp
,
141 struct cat_fork
*rsrcfp
);
143 extern int resolvelink(struct hfsmount
*hfsmp
, u_long linkref
,
144 struct HFSPlusCatalogFile
*recp
);
146 /************************************************************************/
147 /* Entry for searchfs() */
148 /************************************************************************/
150 #define errSearchBufferFull 101 /* Internal search errors */
159 IN struct ucred *cred;
166 struct vop_searchfs_args
*ap
; /*
167 struct vnodeop_desc *a_desc;
169 void *a_searchparams1;
170 void *a_searchparams2;
171 struct attrlist *a_searchattrs;
173 struct timeval *a_timelimit;
174 struct attrlist *a_returnattrs;
175 u_long *a_nummatches;
179 struct searchstate *a_searchstate;
182 ExtendedVCB
*vcb
= VTOVCB(ap
->a_vp
);
184 searchinfospec_t searchInfo1
;
185 searchinfospec_t searchInfo2
;
186 void *attributesBuffer
;
187 void *variableBuffer
;
188 u_long fixedBlockSize
;
189 u_long eachReturnBufferSize
;
190 struct proc
*p
= current_proc();
193 int timerExpired
= false;
194 int doQuickExit
= false;
195 CatalogKey
* myCurrentKeyPtr
;
196 CatalogRecord
* myCurrentDataPtr
;
197 CatPosition
* myCatPositionPtr
;
198 BTScanState myBTScanState
;
199 void *user_start
= NULL
;
202 /* XXX Parameter check a_searchattrs? */
204 *(ap
->a_nummatches
) = 0;
206 if (ap
->a_options
& ~SRCHFS_VALIDOPTIONSMASK
)
209 if (ap
->a_uio
->uio_resid
<= 0)
212 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
214 /* UnPack the search boundries, searchInfo1, searchInfo2 */
215 err
= UnpackSearchAttributeBlock(ap
->a_vp
, ap
->a_searchattrs
,
216 &searchInfo1
, ap
->a_searchparams1
);
218 err
= UnpackSearchAttributeBlock(ap
->a_vp
, ap
->a_searchattrs
,
219 &searchInfo2
, ap
->a_searchparams2
);
222 fixedBlockSize
= sizeof(u_long
) + hfs_attrblksize(ap
->a_returnattrs
); /* u_long for length longword */
223 eachReturnBufferSize
= fixedBlockSize
;
225 if ( ap
->a_returnattrs
->commonattr
& ATTR_CMN_NAME
) /* XXX should be more robust! */
226 eachReturnBufferSize
+= kHFSPlusMaxFileNameBytes
+ 1;
228 MALLOC( attributesBuffer
, void *, eachReturnBufferSize
, M_TEMP
, M_WAITOK
);
229 variableBuffer
= (void*)((char*) attributesBuffer
+ fixedBlockSize
);
231 // XXXdbg - have to lock the user's buffer so we don't fault
232 // while holding the shared catalog file lock. see the comment
233 // in hfs_readdir() for more details.
235 if (VTOHFS(ap
->a_vp
)->jnl
&& ap
->a_uio
->uio_segflg
== UIO_USERSPACE
) {
236 user_start
= ap
->a_uio
->uio_iov
->iov_base
;
237 user_len
= ap
->a_uio
->uio_iov
->iov_len
;
239 if ((err
= vslock(user_start
, user_len
)) != 0) {
241 goto ExitThisRoutine
;
245 /* Lock catalog b-tree */
246 err
= hfs_metafilelocking(VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_SHARED
, p
);
248 goto ExitThisRoutine
;
250 catalogFCB
= GetFileControlBlock(vcb
->catalogRefNum
);
251 myCurrentKeyPtr
= NULL
;
252 myCurrentDataPtr
= NULL
;
253 myCatPositionPtr
= (CatPosition
*)ap
->a_searchstate
;
255 if (ap
->a_options
& SRCHFS_START
) {
256 /* Starting a new search. */
257 /* Make sure the on-disk Catalog file is current */
258 (void) VOP_FSYNC(vcb
->catalogRefNum
, NOCRED
, MNT_WAIT
, p
);
259 ap
->a_options
&= ~SRCHFS_START
;
260 bzero( (caddr_t
)myCatPositionPtr
, sizeof( *myCatPositionPtr
) );
261 err
= BTScanInitialize(catalogFCB
, 0, 0, 0, kCatSearchBufferSize
, &myBTScanState
);
263 #if 1 // Installer workaround (2940423)
264 // hack to get around installer problems when the installer expects search results
265 // to be in key order. At this point the problem appears to be limited to
266 // searches for "Library". The idea here is to go get the "Library" at root
267 // and return it first to the caller then continue the search as normal with
268 // the exception of taking care not to return a duplicate hit (see CheckCriteria)
269 if ( err
== E_NONE
&&
270 (ap
->a_searchattrs
->commonattr
& ATTR_CMN_NAME
) != 0 &&
271 IsTargetName( &searchInfo1
, isHFSPlus
) )
274 BTreeIterator iterator
;
275 FSBufferDescriptor btrec
;
280 bzero( (caddr_t
)&iterator
, sizeof( iterator
) );
281 keyp
= (CatalogKey
*) &iterator
.key
;
282 (void) BuildCatalogKeyUTF8(vcb
, kRootDirID
, "Library", kUndefinedStrLen
, keyp
, NULL
);
284 btrec
.bufferAddress
= &rec
;
286 btrec
.itemSize
= sizeof( rec
);
288 result
= BTSearchRecord( catalogFCB
, &iterator
, &btrec
, &reclen
, &iterator
);
289 if ( result
== E_NONE
) {
290 if (CheckCriteria(vcb
, ap
->a_options
, ap
->a_searchattrs
, &rec
,
291 keyp
, &searchInfo1
, &searchInfo2
, false) &&
292 CheckAccess(vcb
, keyp
, ap
->a_uio
->uio_procp
)) {
294 result
= InsertMatch(ap
->a_vp
, ap
->a_uio
, &rec
,
295 keyp
, ap
->a_returnattrs
,
296 attributesBuffer
, variableBuffer
,
297 eachReturnBufferSize
, ap
->a_nummatches
);
298 if (result
== E_NONE
&& *(ap
->a_nummatches
) >= ap
->a_maxmatches
)
303 #endif // Installer workaround
305 /* Resuming a search. */
306 err
= BTScanInitialize(catalogFCB
, myCatPositionPtr
->nextNode
,
307 myCatPositionPtr
->nextRecord
,
308 myCatPositionPtr
->recordsFound
,
309 kCatSearchBufferSize
,
311 /* Make sure Catalog hasn't changed. */
313 && myCatPositionPtr
->writeCount
!= myBTScanState
.btcb
->writeCount
) {
314 myCatPositionPtr
->writeCount
= myBTScanState
.btcb
->writeCount
;
315 err
= EBUSY
; /* catChangedErr */
319 /* Unlock catalog b-tree */
320 (void) hfs_metafilelocking(VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
322 goto ExitThisRoutine
;
323 #if 1 // Installer workaround (2940423)
326 #endif // Installer workaround
329 * Check all the catalog btree records...
330 * return the attributes for matching items
333 struct timeval myCurrentTime
;
334 struct timeval myElapsedTime
;
336 err
= BTScanNextRecord(&myBTScanState
, timerExpired
,
337 (void **)&myCurrentKeyPtr
, (void **)&myCurrentDataPtr
,
342 /* Resolve any hardlinks */
344 ResolveHardlink(vcb
, (HFSPlusCatalogFile
*) myCurrentDataPtr
);
346 if (CheckCriteria( vcb
, ap
->a_options
, ap
->a_searchattrs
, myCurrentDataPtr
,
347 myCurrentKeyPtr
, &searchInfo1
, &searchInfo2
, true )
348 && CheckAccess(vcb
, myCurrentKeyPtr
, ap
->a_uio
->uio_procp
)) {
349 err
= InsertMatch(ap
->a_vp
, ap
->a_uio
, myCurrentDataPtr
,
350 myCurrentKeyPtr
, ap
->a_returnattrs
,
351 attributesBuffer
, variableBuffer
,
352 eachReturnBufferSize
, ap
->a_nummatches
);
355 * The last match didn't fit so come back
356 * to this record on the next trip.
358 --myBTScanState
.recordsFound
;
359 --myBTScanState
.recordNum
;
363 if (*(ap
->a_nummatches
) >= ap
->a_maxmatches
)
368 * Check our elapsed time and bail if we've hit the max.
369 * The idea here is to throttle the amount of time we
370 * spend in the kernel.
372 myCurrentTime
= time
;
373 timersub(&myCurrentTime
, &myBTScanState
.startTime
, &myElapsedTime
);
374 /* Note: assumes kMaxMicroSecsInKernel is less than 1,000,000 */
375 if (myElapsedTime
.tv_sec
> 0
376 || myElapsedTime
.tv_usec
>= kMaxMicroSecsInKernel
) {
382 /* Update catalog position */
383 myCatPositionPtr
->writeCount
= myBTScanState
.btcb
->writeCount
;
385 BTScanTerminate(&myBTScanState
, &myCatPositionPtr
->nextNode
,
386 &myCatPositionPtr
->nextRecord
,
387 &myCatPositionPtr
->recordsFound
);
389 if ( err
== E_NONE
) {
390 err
= EAGAIN
; /* signal to the user to call searchfs again */
391 } else if ( err
== errSearchBufferFull
) {
392 if ( *(ap
->a_nummatches
) > 0 )
396 } else if ( err
== btNotFound
) {
397 err
= E_NONE
; /* the entire disk has been searched */
398 } else if ( err
== fsBTTimeOutErr
) {
403 FREE( attributesBuffer
, M_TEMP
);
405 if (VTOHFS(ap
->a_vp
)->jnl
&& user_start
) {
406 vsunlock(user_start
, user_len
, TRUE
);
409 return (MacToVFSError(err
));
414 ResolveHardlink(ExtendedVCB
*vcb
, HFSPlusCatalogFile
*recp
)
416 if ((recp
->recordType
== kHFSPlusFileRecord
)
417 && (SWAP_BE32(recp
->userInfo
.fdType
) == kHardLinkFileType
)
418 && (SWAP_BE32(recp
->userInfo
.fdCreator
) == kHFSPlusCreator
)
419 && ((to_bsd_time(recp
->createDate
) == vcb
->vcbCrDate
) ||
420 (to_bsd_time(recp
->createDate
) == VCBTOHFS(vcb
)->hfs_metadata_createdate
))) {
421 (void) resolvelink(VCBTOHFS(vcb
), recp
->bsdInfo
.special
.iNodeNum
, recp
);
427 CompareMasked(const UInt32
*thisValue
, const UInt32
*compareData
,
428 const UInt32
*compareMask
, UInt32 count
)
433 matched
= true; /* Assume it will all match */
435 for (i
=0; i
<count
; i
++) {
436 if (((*thisValue
++ ^ *compareData
++) & *compareMask
++) != 0) {
447 ComparePartialUnicodeName (register ConstUniCharArrayPtr str
, register ItemCount s_len
,
448 register ConstUniCharArrayPtr find
, register ItemCount f_len
)
450 if (f_len
== 0 || s_len
== 0)
456 } while (FastUnicodeCompare(str
++, f_len
, find
, f_len
) != 0);
463 ComparePartialPascalName ( register ConstStr31Param str
, register ConstStr31Param find
)
465 register u_char s_len
= str
[0];
466 register u_char f_len
= find
[0];
467 register u_char
*tsp
;
470 if (f_len
== 0 || s_len
== 0)
473 bcopy(str
, tmpstr
, s_len
+ 1);
476 while (s_len
-- >= f_len
) {
479 if (FastRelString(tsp
++, find
) == 0)
488 * Check to see if caller has access rights to this item
492 CheckAccess(ExtendedVCB
*theVCBPtr
, CatalogKey
*theKeyPtr
, struct proc
*theProcPtr
)
497 HFSCatalogNodeID myNodeID
;
498 unsigned long myPerms
;
499 hfsmount_t
* my_hfsmountPtr
;
500 struct cat_desc my_cat_desc
;
501 struct cat_attr my_cat_attr
;
503 myResult
= 0; /* default to "no access" */
504 my_cat_desc
.cd_nameptr
= NULL
;
505 my_cat_desc
.cd_namelen
= 0;
507 if ( theProcPtr
->p_ucred
->cr_uid
== 0 ) {
508 myResult
= 1; /* allow access */
509 goto ExitThisRoutine
; /* root always has access */
512 my_hfsmountPtr
= VCBTOHFS( theVCBPtr
);
513 isHFSPlus
= ( theVCBPtr
->vcbSigWord
== kHFSPlusSigWord
);
515 myNodeID
= theKeyPtr
->hfsPlus
.parentID
;
517 myNodeID
= theKeyPtr
->hfs
.parentID
;
519 while ( myNodeID
>= kRootDirID
) {
520 /* now go get catalog data for this directory */
521 myErr
= hfs_metafilelocking( my_hfsmountPtr
, kHFSCatalogFileID
, LK_SHARED
, theProcPtr
);
523 goto ExitThisRoutine
; /* no access */
525 myErr
= cat_idlookup( my_hfsmountPtr
, myNodeID
, &my_cat_desc
, &my_cat_attr
, NULL
);
526 (void) hfs_metafilelocking( my_hfsmountPtr
, kHFSCatalogFileID
, LK_RELEASE
, theProcPtr
);
528 goto ExitThisRoutine
; /* no access */
530 myNodeID
= my_cat_desc
.cd_parentcnid
; /* move up the hierarchy */
531 myPerms
= DerivePermissionSummary(my_cat_attr
.ca_uid
, my_cat_attr
.ca_gid
,
532 my_cat_attr
.ca_mode
, my_hfsmountPtr
->hfs_mp
,
533 theProcPtr
->p_ucred
, theProcPtr
);
534 cat_releasedesc( &my_cat_desc
);
536 if ( (myPerms
& X_OK
) == 0 )
537 goto ExitThisRoutine
; /* no access */
540 myResult
= 1; /* allow access */
543 cat_releasedesc( &my_cat_desc
);
549 CheckCriteria( ExtendedVCB
*vcb
,
551 struct attrlist
*attrList
,
554 searchinfospec_t
*searchInfo1
,
555 searchinfospec_t
*searchInfo2
,
558 Boolean matched
, atleastone
;
560 attrgroup_t searchAttributes
;
561 struct cat_attr c_attr
= {0};
562 struct cat_fork datafork
;
563 struct cat_fork rsrcfork
;
565 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
567 switch (rec
->recordType
) {
568 case kHFSFolderRecord
:
569 case kHFSPlusFolderRecord
:
570 if ( (searchBits
& SRCHFS_MATCHDIRS
) == 0 ) { /* If we are NOT searching folders */
577 case kHFSPlusFileRecord
:
578 if ( (searchBits
& SRCHFS_MATCHFILES
) == 0 ) { /* If we are NOT searching files */
584 default: /* Never match a thread record or any other type. */
585 return( false ); /* Not a file or folder record, so can't search it */
588 matched
= true; /* Assume we got a match */
589 atleastone
= false; /* Dont insert unless we match at least one criteria */
591 /* First, attempt to match the name -- either partial or complete */
592 if ( attrList
->commonattr
& ATTR_CMN_NAME
) {
594 /* Check for partial/full HFS Plus name match */
596 if ( searchBits
& SRCHFS_MATCHPARTIALNAMES
) {
597 matched
= ComparePartialUnicodeName(key
->hfsPlus
.nodeName
.unicode
,
598 key
->hfsPlus
.nodeName
.length
,
599 (UniChar
*)searchInfo1
->name
,
600 searchInfo1
->nameLength
);
601 } else /* full HFS Plus name match */ {
602 matched
= (FastUnicodeCompare(key
->hfsPlus
.nodeName
.unicode
,
603 key
->hfsPlus
.nodeName
.length
,
604 (UniChar
*)searchInfo1
->name
,
605 searchInfo1
->nameLength
) == 0);
608 /* Check for partial/full HFS name match */
610 if ( searchBits
& SRCHFS_MATCHPARTIALNAMES
)
611 matched
= ComparePartialPascalName(key
->hfs
.nodeName
, (u_char
*)searchInfo1
->name
);
612 else /* full HFS name match */
613 matched
= (FastRelString(key
->hfs
.nodeName
, (u_char
*)searchInfo1
->name
) == 0);
616 #if 1 // Installer workaround (2940423)
618 HFSCatalogNodeID parentID
;
620 parentID
= key
->hfsPlus
.parentID
;
622 parentID
= key
->hfs
.parentID
;
624 if ( matched
&& parentID
== kRootDirID
&&
625 IsTargetName( searchInfo1
, isHFSPlus
) )
628 #endif // Installer workaround
630 if ( matched
== false || (searchBits
& ~SRCHFS_MATCHPARTIALNAMES
) == 0 )
631 goto TestDone
; /* no match, or nothing more to compare */
636 /* Convert catalog record into cat_attr format. */
637 cat_convertattr(VCBTOHFS(vcb
), rec
, &c_attr
, &datafork
, &rsrcfork
);
639 /* Now that we have a record worth searching, see if it matches the search attributes */
640 if (rec
->recordType
== kHFSFileRecord
||
641 rec
->recordType
== kHFSPlusFileRecord
) {
642 if ((attrList
->fileattr
& ~ATTR_FILE_VALIDMASK
) != 0) { /* attr we do know about */
646 else if ((attrList
->fileattr
& ATTR_FILE_VALIDMASK
) != 0) {
647 searchAttributes
= attrList
->fileattr
;
649 /* File logical length (data fork) */
650 if ( searchAttributes
& ATTR_FILE_DATALENGTH
) {
651 matched
= CompareWideRange(
653 searchInfo1
->f
.dataLogicalLength
,
654 searchInfo2
->f
.dataLogicalLength
);
655 if (matched
== false) goto TestDone
;
659 /* File physical length (data fork) */
660 if ( searchAttributes
& ATTR_FILE_DATAALLOCSIZE
) {
661 matched
= CompareWideRange(
662 (u_int64_t
)datafork
.cf_blocks
* (u_int64_t
)vcb
->blockSize
,
663 searchInfo1
->f
.dataPhysicalLength
,
664 searchInfo2
->f
.dataPhysicalLength
);
665 if (matched
== false) goto TestDone
;
669 /* File logical length (resource fork) */
670 if ( searchAttributes
& ATTR_FILE_RSRCLENGTH
) {
671 matched
= CompareWideRange(
673 searchInfo1
->f
.resourceLogicalLength
,
674 searchInfo2
->f
.resourceLogicalLength
);
675 if (matched
== false) goto TestDone
;
679 /* File physical length (resource fork) */
680 if ( searchAttributes
& ATTR_FILE_RSRCALLOCSIZE
) {
681 matched
= CompareWideRange(
682 (u_int64_t
)rsrcfork
.cf_blocks
* (u_int64_t
)vcb
->blockSize
,
683 searchInfo1
->f
.resourcePhysicalLength
,
684 searchInfo2
->f
.resourcePhysicalLength
);
685 if (matched
== false) goto TestDone
;
690 atleastone
= true; /* to match SRCHFS_MATCHFILES */
694 * Check the directory attributes
696 else if (rec
->recordType
== kHFSFolderRecord
||
697 rec
->recordType
== kHFSPlusFolderRecord
) {
698 if ((attrList
->dirattr
& ~ATTR_DIR_VALIDMASK
) != 0) { /* attr we do know about */
702 else if ((attrList
->dirattr
& ATTR_DIR_VALIDMASK
) != 0) {
703 searchAttributes
= attrList
->dirattr
;
705 /* Directory valence */
706 if ( searchAttributes
& ATTR_DIR_ENTRYCOUNT
) {
707 matched
= CompareRange(c_attr
.ca_entries
,
708 searchInfo1
->d
.numFiles
,
709 searchInfo2
->d
.numFiles
);
710 if (matched
== false) goto TestDone
;
715 atleastone
= true; /* to match SRCHFS_MATCHDIRS */
720 * Check the common attributes
722 searchAttributes
= attrList
->commonattr
;
723 if ( (searchAttributes
& ATTR_CMN_VALIDMASK
) != 0 ) {
725 if ( searchAttributes
& ATTR_CMN_OBJID
) {
726 matched
= CompareRange(c_attr
.ca_fileid
,
728 searchInfo2
->nodeID
);
729 if (matched
== false) goto TestDone
;
734 if ( searchAttributes
& ATTR_CMN_PAROBJID
) {
735 HFSCatalogNodeID parentID
;
738 parentID
= key
->hfsPlus
.parentID
;
740 parentID
= key
->hfs
.parentID
;
742 matched
= CompareRange(parentID
, searchInfo1
->parentDirID
,
743 searchInfo2
->parentDirID
);
744 if (matched
== false) goto TestDone
;
748 /* Finder Info & Extended Finder Info where extFinderInfo is last 32 bytes */
749 if ( searchAttributes
& ATTR_CMN_FNDRINFO
) {
751 thisValue
= (UInt32
*) &c_attr
.ca_finderinfo
;
754 * Note: ioFlFndrInfo and ioDrUsrWds have the same offset in search info, so
755 * no need to test the object type here.
757 matched
= CompareMasked(thisValue
,
758 (UInt32
*)&searchInfo1
->finderInfo
,
759 (UInt32
*) &searchInfo2
->finderInfo
, 8);
760 if (matched
== false) goto TestDone
;
765 if ( searchAttributes
& ATTR_CMN_CRTIME
) {
766 matched
= CompareRange(c_attr
.ca_itime
,
767 searchInfo1
->creationDate
.tv_sec
,
768 searchInfo2
->creationDate
.tv_sec
);
769 if (matched
== false) goto TestDone
;
774 if ( searchAttributes
& ATTR_CMN_MODTIME
) {
775 matched
= CompareRange(c_attr
.ca_mtime
,
776 searchInfo1
->modificationDate
.tv_sec
,
777 searchInfo2
->modificationDate
.tv_sec
);
778 if (matched
== false) goto TestDone
;
783 if ( searchAttributes
& ATTR_CMN_CHGTIME
) {
784 matched
= CompareRange(c_attr
.ca_ctime
,
785 searchInfo1
->changeDate
.tv_sec
,
786 searchInfo2
->changeDate
.tv_sec
);
787 if (matched
== false) goto TestDone
;
792 if ( searchAttributes
& ATTR_CMN_ACCTIME
) {
793 matched
= CompareRange(c_attr
.ca_atime
,
794 searchInfo1
->accessDate
.tv_sec
,
795 searchInfo2
->accessDate
.tv_sec
);
796 if (matched
== false) goto TestDone
;
801 if ( searchAttributes
& ATTR_CMN_BKUPTIME
) {
802 matched
= CompareRange(c_attr
.ca_btime
,
803 searchInfo1
->lastBackupDate
.tv_sec
,
804 searchInfo2
->lastBackupDate
.tv_sec
);
805 if (matched
== false) goto TestDone
;
810 if ( searchAttributes
& ATTR_CMN_OWNERID
) {
811 matched
= CompareRange(c_attr
.ca_uid
,
812 searchInfo1
->uid
, searchInfo2
->uid
);
813 if (matched
== false) goto TestDone
;
818 if ( searchAttributes
& ATTR_CMN_GRPID
) {
819 matched
= CompareRange(c_attr
.ca_gid
,
820 searchInfo1
->gid
, searchInfo2
->gid
);
821 if (matched
== false) goto TestDone
;
826 if ( searchAttributes
& ATTR_CMN_ACCESSMASK
) {
827 matched
= CompareRange((u_long
)c_attr
.ca_mode
,
828 (u_long
)searchInfo1
->mask
,
829 (u_long
)searchInfo2
->mask
);
830 if (matched
== false) goto TestDone
;
835 /* If we got here w/o matching any, then set to false */
841 * Finally, determine whether we need to negate the sense of the match
842 * (i.e. find all objects that DON'T match).
844 if ( searchBits
& SRCHFS_NEGATEPARAMS
)
852 * Adds another record to the packed array for output
855 InsertMatch( struct vnode
*root_vp
, struct uio
*a_uio
, CatalogRecord
*rec
,
856 CatalogKey
*key
, struct attrlist
*returnAttrList
, void *attributesBuffer
,
857 void *variableBuffer
, u_long bufferSize
, u_long
* nummatches
)
860 void *rovingAttributesBuffer
;
861 void *rovingVariableBuffer
;
862 u_long packedBufferSize
;
863 ExtendedVCB
*vcb
= VTOVCB(root_vp
);
864 Boolean isHFSPlus
= vcb
->vcbSigWord
== kHFSPlusSigWord
;
865 u_long privateDir
= VTOHFS(root_vp
)->hfs_private_metadata_dir
;
866 struct attrblock attrblk
;
867 struct cat_desc c_desc
= {0};
868 struct cat_attr c_attr
= {0};
869 struct cat_fork datafork
;
870 struct cat_fork rsrcfork
;
872 rovingAttributesBuffer
= (char*)attributesBuffer
+ sizeof(u_long
); /* Reserve space for length field */
873 rovingVariableBuffer
= variableBuffer
;
875 /* Convert catalog record into cat_attr format. */
876 cat_convertattr(VTOHFS(root_vp
), rec
, &c_attr
, &datafork
, &rsrcfork
);
878 /* hide our private meta data directory */
879 if ((privateDir
!= 0) && (c_attr
.ca_fileid
== privateDir
)) {
884 /* Hide the private journal files */
885 if (VTOHFS(root_vp
)->jnl
&&
886 ((c_attr
.ca_fileid
== VTOHFS(root_vp
)->hfs_jnlfileid
) ||
887 (c_attr
.ca_fileid
== VTOHFS(root_vp
)->hfs_jnlinfoblkid
))) {
892 if (returnAttrList
->commonattr
& ATTR_CMN_NAME
) {
893 cat_convertkey(VTOHFS(root_vp
), key
, rec
, &c_desc
);
895 c_desc
.cd_cnid
= c_attr
.ca_fileid
;
897 c_desc
.cd_parentcnid
= key
->hfsPlus
.parentID
;
899 c_desc
.cd_parentcnid
= key
->hfs
.parentID
;
902 /* hide open files that have been deleted */
903 if ((privateDir
!= 0) && (c_desc
.cd_parentcnid
== privateDir
)) {
908 attrblk
.ab_attrlist
= returnAttrList
;
909 attrblk
.ab_attrbufpp
= &rovingAttributesBuffer
;
910 attrblk
.ab_varbufpp
= &rovingVariableBuffer
;
911 attrblk
.ab_flags
= 0;
912 attrblk
.ab_blocksize
= 0;
914 hfs_packattrblk(&attrblk
, VTOHFS(root_vp
), NULL
, &c_desc
, &c_attr
, &datafork
, &rsrcfork
);
916 packedBufferSize
= (char*)rovingVariableBuffer
- (char*)attributesBuffer
;
918 if ( packedBufferSize
> a_uio
->uio_resid
)
919 return( errSearchBufferFull
);
923 *((u_long
*)attributesBuffer
) = packedBufferSize
; /* Store length of fixed + var block */
925 err
= uiomove( (caddr_t
)attributesBuffer
, packedBufferSize
, a_uio
); /* XXX should be packedBufferSize */
927 cat_releasedesc(&c_desc
);
934 UnpackSearchAttributeBlock( struct vnode
*vp
, struct attrlist
*alist
, searchinfospec_t
*searchInfo
, void *attributeBuffer
)
939 DBG_ASSERT(searchInfo
!= NULL
);
941 bufferSize
= *((u_long
*)attributeBuffer
);
943 return (EINVAL
); /* XXX -DJB is a buffer size of zero ever valid for searchfs? */
945 ++((u_long
*)attributeBuffer
); /* advance past the size */
948 * UnPack common attributes
950 a
= alist
->commonattr
;
952 if ( a
& ATTR_CMN_NAME
) {
953 char *s
= (char*) attributeBuffer
+ ((attrreference_t
*) attributeBuffer
)->attr_dataoffset
;
954 size_t len
= ((attrreference_t
*) attributeBuffer
)->attr_length
;
956 if (len
> sizeof(searchInfo
->name
))
959 if (VTOVCB(vp
)->vcbSigWord
== kHFSPlusSigWord
) {
961 /* Convert name to Unicode to match HFS Plus B-Tree names */
964 if (utf8_decodestr(s
, len
-1, (UniChar
*)searchInfo
->name
, &ucslen
,
965 sizeof(searchInfo
->name
), ':', UTF_DECOMPOSED
))
968 searchInfo
->nameLength
= ucslen
/ sizeof(UniChar
);
970 searchInfo
->nameLength
= 0;
972 ++((attrreference_t
*)attributeBuffer
);
975 /* Convert name to pascal string to match HFS B-Tree names */
978 if (utf8_to_hfs(VTOVCB(vp
), len
-1, s
, (u_char
*)searchInfo
->name
) != 0)
981 searchInfo
->nameLength
= searchInfo
->name
[0];
983 searchInfo
->name
[0] = searchInfo
->nameLength
= 0;
985 ++((attrreference_t
*)attributeBuffer
);
988 if ( a
& ATTR_CMN_OBJID
) {
989 searchInfo
->nodeID
= ((fsobj_id_t
*) attributeBuffer
)->fid_objno
; /* ignore fid_generation */
990 ++((fsobj_id_t
*)attributeBuffer
);
992 if ( a
& ATTR_CMN_PAROBJID
) {
993 searchInfo
->parentDirID
= ((fsobj_id_t
*) attributeBuffer
)->fid_objno
; /* ignore fid_generation */
994 ++((fsobj_id_t
*)attributeBuffer
);
996 if ( a
& ATTR_CMN_CRTIME
) {
997 searchInfo
->creationDate
= *((struct timespec
*)attributeBuffer
);
998 ++((struct timespec
*)attributeBuffer
);
1000 if ( a
& ATTR_CMN_MODTIME
) {
1001 searchInfo
->modificationDate
= *((struct timespec
*)attributeBuffer
);
1002 ++((struct timespec
*)attributeBuffer
);
1004 if ( a
& ATTR_CMN_CHGTIME
) {
1005 searchInfo
->changeDate
= *((struct timespec
*)attributeBuffer
);
1006 ++((struct timespec
*)attributeBuffer
);
1008 if ( a
& ATTR_CMN_ACCTIME
) {
1009 searchInfo
->accessDate
= *((struct timespec
*)attributeBuffer
);
1010 ++((struct timespec
*)attributeBuffer
);
1012 if ( a
& ATTR_CMN_BKUPTIME
) {
1013 searchInfo
->lastBackupDate
= *((struct timespec
*)attributeBuffer
);
1014 ++((struct timespec
*)attributeBuffer
);
1016 if ( a
& ATTR_CMN_FNDRINFO
) {
1017 bcopy( attributeBuffer
, searchInfo
->finderInfo
, sizeof(u_long
) * 8 );
1018 (u_long
*)attributeBuffer
+= 8;
1020 if ( a
& ATTR_CMN_BKUPTIME
) {
1021 searchInfo
->lastBackupDate
= *((struct timespec
*)attributeBuffer
);
1022 ++((struct timespec
*)attributeBuffer
);
1024 if ( a
& ATTR_CMN_OWNERID
) {
1025 searchInfo
->uid
= *((uid_t
*)attributeBuffer
);
1026 ++((uid_t
*)attributeBuffer
);
1028 if ( a
& ATTR_CMN_GRPID
) {
1029 searchInfo
->gid
= *((gid_t
*)attributeBuffer
);
1030 ++((gid_t
*)attributeBuffer
);
1032 if ( a
& ATTR_CMN_ACCESSMASK
) {
1033 searchInfo
->mask
= *((mode_t
*)attributeBuffer
);
1034 ++((mode_t
*)attributeBuffer
);
1040 if ( a
& ATTR_DIR_ENTRYCOUNT
) {
1041 searchInfo
->d
.numFiles
= *((u_long
*)attributeBuffer
);
1042 ++((u_long
*)attributeBuffer
);
1046 a
= alist
->fileattr
;
1048 if ( a
& ATTR_FILE_DATALENGTH
) {
1049 searchInfo
->f
.dataLogicalLength
= *((off_t
*)attributeBuffer
);
1050 ++((off_t
*)attributeBuffer
);
1052 if ( a
& ATTR_FILE_DATAALLOCSIZE
) {
1053 searchInfo
->f
.dataPhysicalLength
= *((off_t
*)attributeBuffer
);
1054 ++((off_t
*)attributeBuffer
);
1056 if ( a
& ATTR_FILE_RSRCLENGTH
) {
1057 searchInfo
->f
.resourceLogicalLength
= *((off_t
*)attributeBuffer
);
1058 ++((off_t
*)attributeBuffer
);
1060 if ( a
& ATTR_FILE_RSRCALLOCSIZE
) {
1061 searchInfo
->f
.resourcePhysicalLength
= *((off_t
*)attributeBuffer
);
1062 ++((off_t
*)attributeBuffer
);
1070 #if 1 // Installer workaround (2940423)
1071 /* this routine was added as part of the work around where some installers would fail */
1072 /* because they incorrectly assumed search results were in some kind of order. */
1073 /* This routine is used to indentify the problematic target. At this point we */
1074 /* only know of one. This routine could be modified for more (I hope not). */
1075 static Boolean
IsTargetName( searchinfospec_t
* searchInfoPtr
, Boolean isHFSPlus
)
1077 if ( searchInfoPtr
->name
== NULL
)
1081 HFSUniStr255 myName
= {
1082 7, /* number of unicode characters */
1084 'L','i','b','r','a','r','y'
1087 if ( FastUnicodeCompare( myName
.unicode
, myName
.length
,
1088 (UniChar
*)searchInfoPtr
->name
,
1089 searchInfoPtr
->nameLength
) == 0 ) {
1094 u_char myName
[32] = {
1095 0x07,'L','i','b','r','a','r','y'
1097 if ( FastRelString(myName
, (u_char
*)searchInfoPtr
->name
) == 0 ) {
1103 } /* IsTargetName */
1104 #endif // Installer workaround