2 * Copyright (c) 1997-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
25 #include <sys/param.h>
26 #include <sys/systm.h>
27 #include <sys/kernel.h>
32 #include <mach/machine/vm_types.h>
33 #include <sys/vnode.h>
34 #include <sys/malloc.h>
35 #include <sys/signalvar.h>
37 #include <sys/utfconv.h>
41 #include "hfs_catalog.h"
42 #include "hfs_attrlist.h"
43 #include "hfs_endian.h"
45 #include "hfscommon/headers/FileMgrInternal.h"
46 #include "hfscommon/headers/CatalogPrivate.h"
47 #include "hfscommon/headers/HFSUnicodeWrappers.h"
48 #include "hfscommon/headers/BTreesPrivate.h"
49 #include "hfscommon/headers/BTreeScanner.h"
52 /* Search criterea. */
53 struct directoryInfoSpec
60 off_t dataLogicalLength
;
61 off_t dataPhysicalLength
;
62 off_t resourceLogicalLength
;
63 off_t resourcePhysicalLength
;
68 u_char name
[kHFSPlusMaxFileNameBytes
];
70 char attributes
; // see IM:Files 2-100
73 struct timespec creationDate
;
74 struct timespec modificationDate
;
75 struct timespec changeDate
;
76 struct timespec accessDate
;
77 struct timespec lastBackupDate
;
82 struct fileInfoSpec f
;
83 struct directoryInfoSpec d
;
85 typedef struct searchinfospec searchinfospec_t
;
87 static void ResolveHardlink(ExtendedVCB
*vcb
, HFSPlusCatalogFile
*recp
);
90 static int UnpackSearchAttributeBlock(struct vnode
*vp
, struct attrlist
*alist
,
91 searchinfospec_t
*searchInfo
, void *attributeBuffer
);
93 static int CheckCriteria( ExtendedVCB
*vcb
,
95 struct attrlist
*attrList
,
98 searchinfospec_t
*searchInfo1
,
99 searchinfospec_t
*searchInfo2
,
100 Boolean lookForDup
);
102 static int CheckAccess(ExtendedVCB
*vcb
, CatalogKey
*key
, struct proc
*p
);
104 static int InsertMatch(struct vnode
*vp
, struct uio
*a_uio
, CatalogRecord
*rec
,
105 CatalogKey
*key
, struct attrlist
*returnAttrList
,
106 void *attributesBuffer
, void *variableBuffer
,
107 u_long bufferSize
, u_long
* nummatches
);
109 static Boolean
CompareRange(u_long val
, u_long low
, u_long high
);
110 static Boolean
CompareWideRange(u_int64_t val
, u_int64_t low
, u_int64_t high
);
112 static Boolean
CompareRange( u_long val
, u_long low
, u_long high
)
114 return( (val
>= low
) && (val
<= high
) );
117 static Boolean
CompareWideRange( u_int64_t val
, u_int64_t low
, u_int64_t high
)
119 return( (val
>= low
) && (val
<= high
) );
121 //#define CompareRange(val, low, high) ((val >= low) && (val <= high))
123 #if 1 // Installer workaround (2940423)
124 static Boolean
IsTargetName( searchinfospec_t
* searchInfoPtr
, Boolean isHFSPlus
);
125 #endif // Installer workaround
127 extern int cat_convertkey(
128 struct hfsmount
*hfsmp
,
130 CatalogRecord
* recp
,
131 struct cat_desc
*descp
);
133 extern void cat_convertattr(
134 struct hfsmount
*hfsmp
,
135 CatalogRecord
* recp
,
136 struct cat_attr
*attrp
,
137 struct cat_fork
*datafp
,
138 struct cat_fork
*rsrcfp
);
140 extern int resolvelink(struct hfsmount
*hfsmp
, u_long linkref
,
141 struct HFSPlusCatalogFile
*recp
);
143 /************************************************************************/
144 /* Entry for searchfs() */
145 /************************************************************************/
147 #define errSearchBufferFull 101 /* Internal search errors */
156 IN struct ucred *cred;
163 struct vop_searchfs_args
*ap
; /*
164 struct vnodeop_desc *a_desc;
166 void *a_searchparams1;
167 void *a_searchparams2;
168 struct attrlist *a_searchattrs;
170 struct timeval *a_timelimit;
171 struct attrlist *a_returnattrs;
172 u_long *a_nummatches;
176 struct searchstate *a_searchstate;
179 ExtendedVCB
*vcb
= VTOVCB(ap
->a_vp
);
181 searchinfospec_t searchInfo1
;
182 searchinfospec_t searchInfo2
;
183 void *attributesBuffer
;
184 void *variableBuffer
;
185 u_long fixedBlockSize
;
186 u_long eachReturnBufferSize
;
187 struct proc
*p
= current_proc();
190 int timerExpired
= false;
191 int doQuickExit
= false;
192 CatalogKey
* myCurrentKeyPtr
;
193 CatalogRecord
* myCurrentDataPtr
;
194 CatPosition
* myCatPositionPtr
;
195 BTScanState myBTScanState
;
196 void *user_start
= NULL
;
199 /* XXX Parameter check a_searchattrs? */
201 *(ap
->a_nummatches
) = 0;
203 if (ap
->a_options
& ~SRCHFS_VALIDOPTIONSMASK
)
206 if (ap
->a_uio
->uio_resid
<= 0)
209 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
211 /* UnPack the search boundries, searchInfo1, searchInfo2 */
212 err
= UnpackSearchAttributeBlock(ap
->a_vp
, ap
->a_searchattrs
,
213 &searchInfo1
, ap
->a_searchparams1
);
215 err
= UnpackSearchAttributeBlock(ap
->a_vp
, ap
->a_searchattrs
,
216 &searchInfo2
, ap
->a_searchparams2
);
219 fixedBlockSize
= sizeof(u_long
) + hfs_attrblksize(ap
->a_returnattrs
); /* u_long for length longword */
220 eachReturnBufferSize
= fixedBlockSize
;
222 if ( ap
->a_returnattrs
->commonattr
& ATTR_CMN_NAME
) /* XXX should be more robust! */
223 eachReturnBufferSize
+= kHFSPlusMaxFileNameBytes
+ 1;
225 MALLOC( attributesBuffer
, void *, eachReturnBufferSize
, M_TEMP
, M_WAITOK
);
226 variableBuffer
= (void*)((char*) attributesBuffer
+ fixedBlockSize
);
228 // XXXdbg - have to lock the user's buffer so we don't fault
229 // while holding the shared catalog file lock. see the comment
230 // in hfs_readdir() for more details.
232 if (VTOHFS(ap
->a_vp
)->jnl
&& ap
->a_uio
->uio_segflg
== UIO_USERSPACE
) {
233 user_start
= ap
->a_uio
->uio_iov
->iov_base
;
234 user_len
= ap
->a_uio
->uio_iov
->iov_len
;
236 if ((err
= vslock(user_start
, user_len
)) != 0) {
238 goto ExitThisRoutine
;
242 /* Lock catalog b-tree */
243 err
= hfs_metafilelocking(VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_SHARED
, p
);
245 goto ExitThisRoutine
;
247 catalogFCB
= GetFileControlBlock(vcb
->catalogRefNum
);
248 myCurrentKeyPtr
= NULL
;
249 myCurrentDataPtr
= NULL
;
250 myCatPositionPtr
= (CatPosition
*)ap
->a_searchstate
;
252 if (ap
->a_options
& SRCHFS_START
) {
253 /* Starting a new search. */
254 /* Make sure the on-disk Catalog file is current */
255 (void) VOP_FSYNC(vcb
->catalogRefNum
, NOCRED
, MNT_WAIT
, p
);
256 ap
->a_options
&= ~SRCHFS_START
;
257 bzero( (caddr_t
)myCatPositionPtr
, sizeof( *myCatPositionPtr
) );
258 err
= BTScanInitialize(catalogFCB
, 0, 0, 0, kCatSearchBufferSize
, &myBTScanState
);
260 #if 1 // Installer workaround (2940423)
261 // hack to get around installer problems when the installer expects search results
262 // to be in key order. At this point the problem appears to be limited to
263 // searches for "Library". The idea here is to go get the "Library" at root
264 // and return it first to the caller then continue the search as normal with
265 // the exception of taking care not to return a duplicate hit (see CheckCriteria)
266 if ( err
== E_NONE
&&
267 (ap
->a_searchattrs
->commonattr
& ATTR_CMN_NAME
) != 0 &&
268 IsTargetName( &searchInfo1
, isHFSPlus
) )
271 BTreeIterator iterator
;
272 FSBufferDescriptor btrec
;
277 bzero( (caddr_t
)&iterator
, sizeof( iterator
) );
278 keyp
= (CatalogKey
*) &iterator
.key
;
279 (void) BuildCatalogKeyUTF8(vcb
, kRootDirID
, "Library", kUndefinedStrLen
, keyp
, NULL
);
281 btrec
.bufferAddress
= &rec
;
283 btrec
.itemSize
= sizeof( rec
);
285 result
= BTSearchRecord( catalogFCB
, &iterator
, &btrec
, &reclen
, &iterator
);
286 if ( result
== E_NONE
) {
287 if (CheckCriteria(vcb
, ap
->a_options
, ap
->a_searchattrs
, &rec
,
288 keyp
, &searchInfo1
, &searchInfo2
, false) &&
289 CheckAccess(vcb
, keyp
, ap
->a_uio
->uio_procp
)) {
291 result
= InsertMatch(ap
->a_vp
, ap
->a_uio
, &rec
,
292 keyp
, ap
->a_returnattrs
,
293 attributesBuffer
, variableBuffer
,
294 eachReturnBufferSize
, ap
->a_nummatches
);
295 if (result
== E_NONE
&& *(ap
->a_nummatches
) >= ap
->a_maxmatches
)
300 #endif // Installer workaround
302 /* Resuming a search. */
303 err
= BTScanInitialize(catalogFCB
, myCatPositionPtr
->nextNode
,
304 myCatPositionPtr
->nextRecord
,
305 myCatPositionPtr
->recordsFound
,
306 kCatSearchBufferSize
,
308 /* Make sure Catalog hasn't changed. */
310 && myCatPositionPtr
->writeCount
!= myBTScanState
.btcb
->writeCount
) {
311 myCatPositionPtr
->writeCount
= myBTScanState
.btcb
->writeCount
;
312 err
= EBUSY
; /* catChangedErr */
316 /* Unlock catalog b-tree */
317 (void) hfs_metafilelocking(VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
319 goto ExitThisRoutine
;
320 #if 1 // Installer workaround (2940423)
323 #endif // Installer workaround
326 * Check all the catalog btree records...
327 * return the attributes for matching items
330 struct timeval myCurrentTime
;
331 struct timeval myElapsedTime
;
333 err
= BTScanNextRecord(&myBTScanState
, timerExpired
,
334 (void **)&myCurrentKeyPtr
, (void **)&myCurrentDataPtr
,
339 /* Resolve any hardlinks */
341 ResolveHardlink(vcb
, (HFSPlusCatalogFile
*) myCurrentDataPtr
);
343 if (CheckCriteria( vcb
, ap
->a_options
, ap
->a_searchattrs
, myCurrentDataPtr
,
344 myCurrentKeyPtr
, &searchInfo1
, &searchInfo2
, true )
345 && CheckAccess(vcb
, myCurrentKeyPtr
, ap
->a_uio
->uio_procp
)) {
346 err
= InsertMatch(ap
->a_vp
, ap
->a_uio
, myCurrentDataPtr
,
347 myCurrentKeyPtr
, ap
->a_returnattrs
,
348 attributesBuffer
, variableBuffer
,
349 eachReturnBufferSize
, ap
->a_nummatches
);
352 * The last match didn't fit so come back
353 * to this record on the next trip.
355 --myBTScanState
.recordsFound
;
356 --myBTScanState
.recordNum
;
360 if (*(ap
->a_nummatches
) >= ap
->a_maxmatches
)
365 * Check our elapsed time and bail if we've hit the max.
366 * The idea here is to throttle the amount of time we
367 * spend in the kernel.
369 myCurrentTime
= time
;
370 timersub(&myCurrentTime
, &myBTScanState
.startTime
, &myElapsedTime
);
371 /* Note: assumes kMaxMicroSecsInKernel is less than 1,000,000 */
372 if (myElapsedTime
.tv_sec
> 0
373 || myElapsedTime
.tv_usec
>= kMaxMicroSecsInKernel
) {
379 /* Update catalog position */
380 myCatPositionPtr
->writeCount
= myBTScanState
.btcb
->writeCount
;
382 BTScanTerminate(&myBTScanState
, &myCatPositionPtr
->nextNode
,
383 &myCatPositionPtr
->nextRecord
,
384 &myCatPositionPtr
->recordsFound
);
386 if ( err
== E_NONE
) {
387 err
= EAGAIN
; /* signal to the user to call searchfs again */
388 } else if ( err
== errSearchBufferFull
) {
389 if ( *(ap
->a_nummatches
) > 0 )
393 } else if ( err
== btNotFound
) {
394 err
= E_NONE
; /* the entire disk has been searched */
395 } else if ( err
== fsBTTimeOutErr
) {
400 FREE( attributesBuffer
, M_TEMP
);
402 if (VTOHFS(ap
->a_vp
)->jnl
&& user_start
) {
403 vsunlock(user_start
, user_len
, TRUE
);
406 return (MacToVFSError(err
));
411 ResolveHardlink(ExtendedVCB
*vcb
, HFSPlusCatalogFile
*recp
)
413 if ((recp
->recordType
== kHFSPlusFileRecord
)
414 && (SWAP_BE32(recp
->userInfo
.fdType
) == kHardLinkFileType
)
415 && (SWAP_BE32(recp
->userInfo
.fdCreator
) == kHFSPlusCreator
)
416 && ((to_bsd_time(recp
->createDate
) == vcb
->vcbCrDate
) ||
417 (to_bsd_time(recp
->createDate
) == VCBTOHFS(vcb
)->hfs_metadata_createdate
))) {
418 (void) resolvelink(VCBTOHFS(vcb
), recp
->bsdInfo
.special
.iNodeNum
, recp
);
424 CompareMasked(const UInt32
*thisValue
, const UInt32
*compareData
,
425 const UInt32
*compareMask
, UInt32 count
)
430 matched
= true; /* Assume it will all match */
432 for (i
=0; i
<count
; i
++) {
433 if (((*thisValue
++ ^ *compareData
++) & *compareMask
++) != 0) {
444 ComparePartialUnicodeName (register ConstUniCharArrayPtr str
, register ItemCount s_len
,
445 register ConstUniCharArrayPtr find
, register ItemCount f_len
)
447 if (f_len
== 0 || s_len
== 0)
453 } while (FastUnicodeCompare(str
++, f_len
, find
, f_len
) != 0);
460 ComparePartialPascalName ( register ConstStr31Param str
, register ConstStr31Param find
)
462 register u_char s_len
= str
[0];
463 register u_char f_len
= find
[0];
464 register u_char
*tsp
;
467 if (f_len
== 0 || s_len
== 0)
470 bcopy(str
, tmpstr
, s_len
+ 1);
473 while (s_len
-- >= f_len
) {
476 if (FastRelString(tsp
++, find
) == 0)
485 * Check to see if caller has access rights to this item
489 CheckAccess(ExtendedVCB
*theVCBPtr
, CatalogKey
*theKeyPtr
, struct proc
*theProcPtr
)
494 HFSCatalogNodeID myNodeID
;
495 unsigned long myPerms
;
496 hfsmount_t
* my_hfsmountPtr
;
497 struct cat_desc my_cat_desc
;
498 struct cat_attr my_cat_attr
;
500 myResult
= 0; /* default to "no access" */
501 my_cat_desc
.cd_nameptr
= NULL
;
502 my_cat_desc
.cd_namelen
= 0;
504 if ( theProcPtr
->p_ucred
->cr_uid
== 0 ) {
505 myResult
= 1; /* allow access */
506 goto ExitThisRoutine
; /* root always has access */
509 my_hfsmountPtr
= VCBTOHFS( theVCBPtr
);
510 isHFSPlus
= ( theVCBPtr
->vcbSigWord
== kHFSPlusSigWord
);
512 myNodeID
= theKeyPtr
->hfsPlus
.parentID
;
514 myNodeID
= theKeyPtr
->hfs
.parentID
;
516 while ( myNodeID
>= kRootDirID
) {
517 /* now go get catalog data for this directory */
518 myErr
= hfs_metafilelocking( my_hfsmountPtr
, kHFSCatalogFileID
, LK_SHARED
, theProcPtr
);
520 goto ExitThisRoutine
; /* no access */
522 myErr
= cat_idlookup( my_hfsmountPtr
, myNodeID
, &my_cat_desc
, &my_cat_attr
, NULL
);
523 (void) hfs_metafilelocking( my_hfsmountPtr
, kHFSCatalogFileID
, LK_RELEASE
, theProcPtr
);
525 goto ExitThisRoutine
; /* no access */
527 myNodeID
= my_cat_desc
.cd_parentcnid
; /* move up the hierarchy */
528 myPerms
= DerivePermissionSummary(my_cat_attr
.ca_uid
, my_cat_attr
.ca_gid
,
529 my_cat_attr
.ca_mode
, my_hfsmountPtr
->hfs_mp
,
530 theProcPtr
->p_ucred
, theProcPtr
);
531 cat_releasedesc( &my_cat_desc
);
533 if ( (myPerms
& X_OK
) == 0 )
534 goto ExitThisRoutine
; /* no access */
537 myResult
= 1; /* allow access */
540 cat_releasedesc( &my_cat_desc
);
546 CheckCriteria( ExtendedVCB
*vcb
,
548 struct attrlist
*attrList
,
551 searchinfospec_t
*searchInfo1
,
552 searchinfospec_t
*searchInfo2
,
555 Boolean matched
, atleastone
;
557 attrgroup_t searchAttributes
;
558 struct cat_attr c_attr
= {0};
559 struct cat_fork datafork
;
560 struct cat_fork rsrcfork
;
562 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
564 switch (rec
->recordType
) {
565 case kHFSFolderRecord
:
566 case kHFSPlusFolderRecord
:
567 if ( (searchBits
& SRCHFS_MATCHDIRS
) == 0 ) { /* If we are NOT searching folders */
574 case kHFSPlusFileRecord
:
575 if ( (searchBits
& SRCHFS_MATCHFILES
) == 0 ) { /* If we are NOT searching files */
581 default: /* Never match a thread record or any other type. */
582 return( false ); /* Not a file or folder record, so can't search it */
585 matched
= true; /* Assume we got a match */
586 atleastone
= false; /* Dont insert unless we match at least one criteria */
588 /* First, attempt to match the name -- either partial or complete */
589 if ( attrList
->commonattr
& ATTR_CMN_NAME
) {
591 /* Check for partial/full HFS Plus name match */
593 if ( searchBits
& SRCHFS_MATCHPARTIALNAMES
) {
594 matched
= ComparePartialUnicodeName(key
->hfsPlus
.nodeName
.unicode
,
595 key
->hfsPlus
.nodeName
.length
,
596 (UniChar
*)searchInfo1
->name
,
597 searchInfo1
->nameLength
);
598 } else /* full HFS Plus name match */ {
599 matched
= (FastUnicodeCompare(key
->hfsPlus
.nodeName
.unicode
,
600 key
->hfsPlus
.nodeName
.length
,
601 (UniChar
*)searchInfo1
->name
,
602 searchInfo1
->nameLength
) == 0);
605 /* Check for partial/full HFS name match */
607 if ( searchBits
& SRCHFS_MATCHPARTIALNAMES
)
608 matched
= ComparePartialPascalName(key
->hfs
.nodeName
, (u_char
*)searchInfo1
->name
);
609 else /* full HFS name match */
610 matched
= (FastRelString(key
->hfs
.nodeName
, (u_char
*)searchInfo1
->name
) == 0);
613 #if 1 // Installer workaround (2940423)
615 HFSCatalogNodeID parentID
;
617 parentID
= key
->hfsPlus
.parentID
;
619 parentID
= key
->hfs
.parentID
;
621 if ( matched
&& parentID
== kRootDirID
&&
622 IsTargetName( searchInfo1
, isHFSPlus
) )
625 #endif // Installer workaround
627 if ( matched
== false || (searchBits
& ~SRCHFS_MATCHPARTIALNAMES
) == 0 )
628 goto TestDone
; /* no match, or nothing more to compare */
633 /* Convert catalog record into cat_attr format. */
634 cat_convertattr(VCBTOHFS(vcb
), rec
, &c_attr
, &datafork
, &rsrcfork
);
636 /* Now that we have a record worth searching, see if it matches the search attributes */
637 if (rec
->recordType
== kHFSFileRecord
||
638 rec
->recordType
== kHFSPlusFileRecord
) {
639 if ((attrList
->fileattr
& ~ATTR_FILE_VALIDMASK
) != 0) { /* attr we do know about */
643 else if ((attrList
->fileattr
& ATTR_FILE_VALIDMASK
) != 0) {
644 searchAttributes
= attrList
->fileattr
;
646 /* File logical length (data fork) */
647 if ( searchAttributes
& ATTR_FILE_DATALENGTH
) {
648 matched
= CompareWideRange(
650 searchInfo1
->f
.dataLogicalLength
,
651 searchInfo2
->f
.dataLogicalLength
);
652 if (matched
== false) goto TestDone
;
656 /* File physical length (data fork) */
657 if ( searchAttributes
& ATTR_FILE_DATAALLOCSIZE
) {
658 matched
= CompareWideRange(
659 (u_int64_t
)datafork
.cf_blocks
* (u_int64_t
)vcb
->blockSize
,
660 searchInfo1
->f
.dataPhysicalLength
,
661 searchInfo2
->f
.dataPhysicalLength
);
662 if (matched
== false) goto TestDone
;
666 /* File logical length (resource fork) */
667 if ( searchAttributes
& ATTR_FILE_RSRCLENGTH
) {
668 matched
= CompareWideRange(
670 searchInfo1
->f
.resourceLogicalLength
,
671 searchInfo2
->f
.resourceLogicalLength
);
672 if (matched
== false) goto TestDone
;
676 /* File physical length (resource fork) */
677 if ( searchAttributes
& ATTR_FILE_RSRCALLOCSIZE
) {
678 matched
= CompareWideRange(
679 (u_int64_t
)rsrcfork
.cf_blocks
* (u_int64_t
)vcb
->blockSize
,
680 searchInfo1
->f
.resourcePhysicalLength
,
681 searchInfo2
->f
.resourcePhysicalLength
);
682 if (matched
== false) goto TestDone
;
687 atleastone
= true; /* to match SRCHFS_MATCHFILES */
691 * Check the directory attributes
693 else if (rec
->recordType
== kHFSFolderRecord
||
694 rec
->recordType
== kHFSPlusFolderRecord
) {
695 if ((attrList
->dirattr
& ~ATTR_DIR_VALIDMASK
) != 0) { /* attr we do know about */
699 else if ((attrList
->dirattr
& ATTR_DIR_VALIDMASK
) != 0) {
700 searchAttributes
= attrList
->dirattr
;
702 /* Directory valence */
703 if ( searchAttributes
& ATTR_DIR_ENTRYCOUNT
) {
704 matched
= CompareRange(c_attr
.ca_entries
,
705 searchInfo1
->d
.numFiles
,
706 searchInfo2
->d
.numFiles
);
707 if (matched
== false) goto TestDone
;
712 atleastone
= true; /* to match SRCHFS_MATCHDIRS */
717 * Check the common attributes
719 searchAttributes
= attrList
->commonattr
;
720 if ( (searchAttributes
& ATTR_CMN_VALIDMASK
) != 0 ) {
722 if ( searchAttributes
& ATTR_CMN_OBJID
) {
723 matched
= CompareRange(c_attr
.ca_fileid
,
725 searchInfo2
->nodeID
);
726 if (matched
== false) goto TestDone
;
731 if ( searchAttributes
& ATTR_CMN_PAROBJID
) {
732 HFSCatalogNodeID parentID
;
735 parentID
= key
->hfsPlus
.parentID
;
737 parentID
= key
->hfs
.parentID
;
739 matched
= CompareRange(parentID
, searchInfo1
->parentDirID
,
740 searchInfo2
->parentDirID
);
741 if (matched
== false) goto TestDone
;
745 /* Finder Info & Extended Finder Info where extFinderInfo is last 32 bytes */
746 if ( searchAttributes
& ATTR_CMN_FNDRINFO
) {
748 thisValue
= (UInt32
*) &c_attr
.ca_finderinfo
;
751 * Note: ioFlFndrInfo and ioDrUsrWds have the same offset in search info, so
752 * no need to test the object type here.
754 matched
= CompareMasked(thisValue
,
755 (UInt32
*)&searchInfo1
->finderInfo
,
756 (UInt32
*) &searchInfo2
->finderInfo
, 8);
757 if (matched
== false) goto TestDone
;
762 if ( searchAttributes
& ATTR_CMN_CRTIME
) {
763 matched
= CompareRange(c_attr
.ca_itime
,
764 searchInfo1
->creationDate
.tv_sec
,
765 searchInfo2
->creationDate
.tv_sec
);
766 if (matched
== false) goto TestDone
;
771 if ( searchAttributes
& ATTR_CMN_MODTIME
) {
772 matched
= CompareRange(c_attr
.ca_mtime
,
773 searchInfo1
->modificationDate
.tv_sec
,
774 searchInfo2
->modificationDate
.tv_sec
);
775 if (matched
== false) goto TestDone
;
780 if ( searchAttributes
& ATTR_CMN_CHGTIME
) {
781 matched
= CompareRange(c_attr
.ca_ctime
,
782 searchInfo1
->changeDate
.tv_sec
,
783 searchInfo2
->changeDate
.tv_sec
);
784 if (matched
== false) goto TestDone
;
789 if ( searchAttributes
& ATTR_CMN_ACCTIME
) {
790 matched
= CompareRange(c_attr
.ca_atime
,
791 searchInfo1
->accessDate
.tv_sec
,
792 searchInfo2
->accessDate
.tv_sec
);
793 if (matched
== false) goto TestDone
;
798 if ( searchAttributes
& ATTR_CMN_BKUPTIME
) {
799 matched
= CompareRange(c_attr
.ca_btime
,
800 searchInfo1
->lastBackupDate
.tv_sec
,
801 searchInfo2
->lastBackupDate
.tv_sec
);
802 if (matched
== false) goto TestDone
;
807 if ( searchAttributes
& ATTR_CMN_OWNERID
) {
808 matched
= CompareRange(c_attr
.ca_uid
,
809 searchInfo1
->uid
, searchInfo2
->uid
);
810 if (matched
== false) goto TestDone
;
815 if ( searchAttributes
& ATTR_CMN_GRPID
) {
816 matched
= CompareRange(c_attr
.ca_gid
,
817 searchInfo1
->gid
, searchInfo2
->gid
);
818 if (matched
== false) goto TestDone
;
823 if ( searchAttributes
& ATTR_CMN_ACCESSMASK
) {
824 matched
= CompareRange((u_long
)c_attr
.ca_mode
,
825 (u_long
)searchInfo1
->mask
,
826 (u_long
)searchInfo2
->mask
);
827 if (matched
== false) goto TestDone
;
832 /* If we got here w/o matching any, then set to false */
838 * Finally, determine whether we need to negate the sense of the match
839 * (i.e. find all objects that DON'T match).
841 if ( searchBits
& SRCHFS_NEGATEPARAMS
)
849 * Adds another record to the packed array for output
852 InsertMatch( struct vnode
*root_vp
, struct uio
*a_uio
, CatalogRecord
*rec
,
853 CatalogKey
*key
, struct attrlist
*returnAttrList
, void *attributesBuffer
,
854 void *variableBuffer
, u_long bufferSize
, u_long
* nummatches
)
857 void *rovingAttributesBuffer
;
858 void *rovingVariableBuffer
;
859 u_long packedBufferSize
;
860 ExtendedVCB
*vcb
= VTOVCB(root_vp
);
861 Boolean isHFSPlus
= vcb
->vcbSigWord
== kHFSPlusSigWord
;
862 u_long privateDir
= VTOHFS(root_vp
)->hfs_private_metadata_dir
;
863 struct attrblock attrblk
;
864 struct cat_desc c_desc
= {0};
865 struct cat_attr c_attr
= {0};
866 struct cat_fork datafork
;
867 struct cat_fork rsrcfork
;
869 rovingAttributesBuffer
= (char*)attributesBuffer
+ sizeof(u_long
); /* Reserve space for length field */
870 rovingVariableBuffer
= variableBuffer
;
872 /* Convert catalog record into cat_attr format. */
873 cat_convertattr(VTOHFS(root_vp
), rec
, &c_attr
, &datafork
, &rsrcfork
);
875 /* hide our private meta data directory */
876 if ((privateDir
!= 0) && (c_attr
.ca_fileid
== privateDir
)) {
881 /* Hide the private journal files */
882 if (VTOHFS(root_vp
)->jnl
&&
883 ((c_attr
.ca_fileid
== VTOHFS(root_vp
)->hfs_jnlfileid
) ||
884 (c_attr
.ca_fileid
== VTOHFS(root_vp
)->hfs_jnlinfoblkid
))) {
889 if (returnAttrList
->commonattr
& ATTR_CMN_NAME
) {
890 cat_convertkey(VTOHFS(root_vp
), key
, rec
, &c_desc
);
892 c_desc
.cd_cnid
= c_attr
.ca_fileid
;
894 c_desc
.cd_parentcnid
= key
->hfsPlus
.parentID
;
896 c_desc
.cd_parentcnid
= key
->hfs
.parentID
;
899 /* hide open files that have been deleted */
900 if ((privateDir
!= 0) && (c_desc
.cd_parentcnid
== privateDir
)) {
905 attrblk
.ab_attrlist
= returnAttrList
;
906 attrblk
.ab_attrbufpp
= &rovingAttributesBuffer
;
907 attrblk
.ab_varbufpp
= &rovingVariableBuffer
;
908 attrblk
.ab_flags
= 0;
909 attrblk
.ab_blocksize
= 0;
911 hfs_packattrblk(&attrblk
, VTOHFS(root_vp
), NULL
, &c_desc
, &c_attr
, &datafork
, &rsrcfork
);
913 packedBufferSize
= (char*)rovingVariableBuffer
- (char*)attributesBuffer
;
915 if ( packedBufferSize
> a_uio
->uio_resid
)
916 return( errSearchBufferFull
);
920 *((u_long
*)attributesBuffer
) = packedBufferSize
; /* Store length of fixed + var block */
922 err
= uiomove( (caddr_t
)attributesBuffer
, packedBufferSize
, a_uio
); /* XXX should be packedBufferSize */
924 cat_releasedesc(&c_desc
);
931 UnpackSearchAttributeBlock( struct vnode
*vp
, struct attrlist
*alist
, searchinfospec_t
*searchInfo
, void *attributeBuffer
)
936 DBG_ASSERT(searchInfo
!= NULL
);
938 bufferSize
= *((u_long
*)attributeBuffer
);
940 return (EINVAL
); /* XXX -DJB is a buffer size of zero ever valid for searchfs? */
942 ++((u_long
*)attributeBuffer
); /* advance past the size */
945 * UnPack common attributes
947 a
= alist
->commonattr
;
949 if ( a
& ATTR_CMN_NAME
) {
950 char *s
= (char*) attributeBuffer
+ ((attrreference_t
*) attributeBuffer
)->attr_dataoffset
;
951 size_t len
= ((attrreference_t
*) attributeBuffer
)->attr_length
;
953 if (len
> sizeof(searchInfo
->name
))
956 if (VTOVCB(vp
)->vcbSigWord
== kHFSPlusSigWord
) {
958 /* Convert name to Unicode to match HFS Plus B-Tree names */
961 if (utf8_decodestr(s
, len
-1, (UniChar
*)searchInfo
->name
, &ucslen
,
962 sizeof(searchInfo
->name
), ':', UTF_DECOMPOSED
))
965 searchInfo
->nameLength
= ucslen
/ sizeof(UniChar
);
967 searchInfo
->nameLength
= 0;
969 ++((attrreference_t
*)attributeBuffer
);
972 /* Convert name to pascal string to match HFS B-Tree names */
975 if (utf8_to_hfs(VTOVCB(vp
), len
-1, s
, (u_char
*)searchInfo
->name
) != 0)
978 searchInfo
->nameLength
= searchInfo
->name
[0];
980 searchInfo
->name
[0] = searchInfo
->nameLength
= 0;
982 ++((attrreference_t
*)attributeBuffer
);
985 if ( a
& ATTR_CMN_OBJID
) {
986 searchInfo
->nodeID
= ((fsobj_id_t
*) attributeBuffer
)->fid_objno
; /* ignore fid_generation */
987 ++((fsobj_id_t
*)attributeBuffer
);
989 if ( a
& ATTR_CMN_PAROBJID
) {
990 searchInfo
->parentDirID
= ((fsobj_id_t
*) attributeBuffer
)->fid_objno
; /* ignore fid_generation */
991 ++((fsobj_id_t
*)attributeBuffer
);
993 if ( a
& ATTR_CMN_CRTIME
) {
994 searchInfo
->creationDate
= *((struct timespec
*)attributeBuffer
);
995 ++((struct timespec
*)attributeBuffer
);
997 if ( a
& ATTR_CMN_MODTIME
) {
998 searchInfo
->modificationDate
= *((struct timespec
*)attributeBuffer
);
999 ++((struct timespec
*)attributeBuffer
);
1001 if ( a
& ATTR_CMN_CHGTIME
) {
1002 searchInfo
->changeDate
= *((struct timespec
*)attributeBuffer
);
1003 ++((struct timespec
*)attributeBuffer
);
1005 if ( a
& ATTR_CMN_ACCTIME
) {
1006 searchInfo
->accessDate
= *((struct timespec
*)attributeBuffer
);
1007 ++((struct timespec
*)attributeBuffer
);
1009 if ( a
& ATTR_CMN_BKUPTIME
) {
1010 searchInfo
->lastBackupDate
= *((struct timespec
*)attributeBuffer
);
1011 ++((struct timespec
*)attributeBuffer
);
1013 if ( a
& ATTR_CMN_FNDRINFO
) {
1014 bcopy( attributeBuffer
, searchInfo
->finderInfo
, sizeof(u_long
) * 8 );
1015 (u_long
*)attributeBuffer
+= 8;
1017 if ( a
& ATTR_CMN_BKUPTIME
) {
1018 searchInfo
->lastBackupDate
= *((struct timespec
*)attributeBuffer
);
1019 ++((struct timespec
*)attributeBuffer
);
1021 if ( a
& ATTR_CMN_OWNERID
) {
1022 searchInfo
->uid
= *((uid_t
*)attributeBuffer
);
1023 ++((uid_t
*)attributeBuffer
);
1025 if ( a
& ATTR_CMN_GRPID
) {
1026 searchInfo
->gid
= *((gid_t
*)attributeBuffer
);
1027 ++((gid_t
*)attributeBuffer
);
1029 if ( a
& ATTR_CMN_ACCESSMASK
) {
1030 searchInfo
->mask
= *((mode_t
*)attributeBuffer
);
1031 ++((mode_t
*)attributeBuffer
);
1037 if ( a
& ATTR_DIR_ENTRYCOUNT
) {
1038 searchInfo
->d
.numFiles
= *((u_long
*)attributeBuffer
);
1039 ++((u_long
*)attributeBuffer
);
1043 a
= alist
->fileattr
;
1045 if ( a
& ATTR_FILE_DATALENGTH
) {
1046 searchInfo
->f
.dataLogicalLength
= *((off_t
*)attributeBuffer
);
1047 ++((off_t
*)attributeBuffer
);
1049 if ( a
& ATTR_FILE_DATAALLOCSIZE
) {
1050 searchInfo
->f
.dataPhysicalLength
= *((off_t
*)attributeBuffer
);
1051 ++((off_t
*)attributeBuffer
);
1053 if ( a
& ATTR_FILE_RSRCLENGTH
) {
1054 searchInfo
->f
.resourceLogicalLength
= *((off_t
*)attributeBuffer
);
1055 ++((off_t
*)attributeBuffer
);
1057 if ( a
& ATTR_FILE_RSRCALLOCSIZE
) {
1058 searchInfo
->f
.resourcePhysicalLength
= *((off_t
*)attributeBuffer
);
1059 ++((off_t
*)attributeBuffer
);
1067 #if 1 // Installer workaround (2940423)
1068 /* this routine was added as part of the work around where some installers would fail */
1069 /* because they incorrectly assumed search results were in some kind of order. */
1070 /* This routine is used to indentify the problematic target. At this point we */
1071 /* only know of one. This routine could be modified for more (I hope not). */
1072 static Boolean
IsTargetName( searchinfospec_t
* searchInfoPtr
, Boolean isHFSPlus
)
1074 if ( searchInfoPtr
->name
== NULL
)
1078 HFSUniStr255 myName
= {
1079 7, /* number of unicode characters */
1081 'L','i','b','r','a','r','y'
1084 if ( FastUnicodeCompare( myName
.unicode
, myName
.length
,
1085 (UniChar
*)searchInfoPtr
->name
,
1086 searchInfoPtr
->nameLength
) == 0 ) {
1091 u_char myName
[32] = {
1092 0x07,'L','i','b','r','a','r','y'
1094 if ( FastRelString(myName
, (u_char
*)searchInfoPtr
->name
) == 0 ) {
1100 } /* IsTargetName */
1101 #endif // Installer workaround