2 * Copyright (c) 1997-2003 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
, u_long searchBits
, 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;
164 struct vop_searchfs_args
*ap
; /*
165 struct vnodeop_desc *a_desc;
167 void *a_searchparams1;
168 void *a_searchparams2;
169 struct attrlist *a_searchattrs;
171 struct timeval *a_timelimit;
172 struct attrlist *a_returnattrs;
173 u_long *a_nummatches;
177 struct searchstate *a_searchstate;
180 ExtendedVCB
*vcb
= VTOVCB(ap
->a_vp
);
182 searchinfospec_t searchInfo1
;
183 searchinfospec_t searchInfo2
;
184 void *attributesBuffer
;
185 void *variableBuffer
;
186 u_long fixedBlockSize
;
187 u_long eachReturnBufferSize
;
188 struct proc
*p
= current_proc();
191 int timerExpired
= false;
192 int doQuickExit
= false;
193 CatalogKey
* myCurrentKeyPtr
;
194 CatalogRecord
* myCurrentDataPtr
;
195 CatPosition
* myCatPositionPtr
;
196 BTScanState myBTScanState
;
197 void *user_start
= NULL
;
201 /* XXX Parameter check a_searchattrs? */
203 *(ap
->a_nummatches
) = 0;
205 if (ap
->a_options
& ~SRCHFS_VALIDOPTIONSMASK
)
208 /* SRCHFS_SKIPLINKS requires root access.
209 * This option cannot be used with either
210 * the ATTR_CMN_NAME or ATTR_CMN_PAROBJID
213 if (ap
->a_options
& SRCHFS_SKIPLINKS
) {
216 attrs
= ap
->a_searchattrs
->commonattr
| ap
->a_returnattrs
->commonattr
;
217 if (attrs
& (ATTR_CMN_NAME
| ATTR_CMN_PAROBJID
))
219 if ((err
= suser(p
->p_ucred
, &p
->p_acflag
)))
223 if (ap
->a_uio
->uio_resid
<= 0)
226 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
228 searchTime
= kMaxMicroSecsInKernel
;
229 if (ap
->a_timelimit
->tv_sec
== 0 &&
230 ap
->a_timelimit
->tv_usec
> 0 &&
231 ap
->a_timelimit
->tv_usec
< kMaxMicroSecsInKernel
) {
232 searchTime
= ap
->a_timelimit
->tv_usec
;
235 /* UnPack the search boundries, searchInfo1, searchInfo2 */
236 err
= UnpackSearchAttributeBlock(ap
->a_vp
, ap
->a_searchattrs
,
237 &searchInfo1
, ap
->a_searchparams1
);
239 err
= UnpackSearchAttributeBlock(ap
->a_vp
, ap
->a_searchattrs
,
240 &searchInfo2
, ap
->a_searchparams2
);
243 fixedBlockSize
= sizeof(u_long
) + hfs_attrblksize(ap
->a_returnattrs
); /* u_long for length longword */
244 eachReturnBufferSize
= fixedBlockSize
;
246 if ( ap
->a_returnattrs
->commonattr
& ATTR_CMN_NAME
) /* XXX should be more robust! */
247 eachReturnBufferSize
+= kHFSPlusMaxFileNameBytes
+ 1;
249 MALLOC( attributesBuffer
, void *, eachReturnBufferSize
, M_TEMP
, M_WAITOK
);
250 variableBuffer
= (void*)((char*) attributesBuffer
+ fixedBlockSize
);
252 // XXXdbg - have to lock the user's buffer so we don't fault
253 // while holding the shared catalog file lock. see the comment
254 // in hfs_readdir() for more details.
256 if (VTOHFS(ap
->a_vp
)->jnl
&& ap
->a_uio
->uio_segflg
== UIO_USERSPACE
) {
257 user_start
= ap
->a_uio
->uio_iov
->iov_base
;
258 user_len
= ap
->a_uio
->uio_iov
->iov_len
;
260 if ((err
= vslock(user_start
, user_len
)) != 0) {
262 goto ExitThisRoutine
;
266 /* Lock catalog b-tree */
267 err
= hfs_metafilelocking(VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_SHARED
, p
);
269 goto ExitThisRoutine
;
271 catalogFCB
= GetFileControlBlock(vcb
->catalogRefNum
);
272 myCurrentKeyPtr
= NULL
;
273 myCurrentDataPtr
= NULL
;
274 myCatPositionPtr
= (CatPosition
*)ap
->a_searchstate
;
276 if (ap
->a_options
& SRCHFS_START
) {
277 /* Starting a new search. */
278 /* Make sure the on-disk Catalog file is current */
279 (void) VOP_FSYNC(vcb
->catalogRefNum
, NOCRED
, MNT_WAIT
, p
);
280 if (VTOHFS(ap
->a_vp
)->jnl
) {
281 journal_flush(VTOHFS(ap
->a_vp
)->jnl
);
284 ap
->a_options
&= ~SRCHFS_START
;
285 bzero( (caddr_t
)myCatPositionPtr
, sizeof( *myCatPositionPtr
) );
286 err
= BTScanInitialize(catalogFCB
, 0, 0, 0, kCatSearchBufferSize
, &myBTScanState
);
288 #if 1 // Installer workaround (2940423)
289 // hack to get around installer problems when the installer expects search results
290 // to be in key order. At this point the problem appears to be limited to
291 // searches for "Library". The idea here is to go get the "Library" at root
292 // and return it first to the caller then continue the search as normal with
293 // the exception of taking care not to return a duplicate hit (see CheckCriteria)
294 if ( err
== E_NONE
&&
295 (ap
->a_searchattrs
->commonattr
& ATTR_CMN_NAME
) != 0 &&
296 IsTargetName( &searchInfo1
, isHFSPlus
) )
299 BTreeIterator iterator
;
300 FSBufferDescriptor btrec
;
305 bzero( (caddr_t
)&iterator
, sizeof( iterator
) );
306 keyp
= (CatalogKey
*) &iterator
.key
;
307 (void) BuildCatalogKeyUTF8(vcb
, kRootDirID
, "Library", kUndefinedStrLen
, keyp
, NULL
);
309 btrec
.bufferAddress
= &rec
;
311 btrec
.itemSize
= sizeof( rec
);
313 result
= BTSearchRecord( catalogFCB
, &iterator
, &btrec
, &reclen
, &iterator
);
314 if ( result
== E_NONE
) {
315 if (CheckCriteria(vcb
, ap
->a_options
, ap
->a_searchattrs
, &rec
,
316 keyp
, &searchInfo1
, &searchInfo2
, false) &&
317 CheckAccess(vcb
, ap
->a_options
, keyp
, ap
->a_uio
->uio_procp
)) {
319 result
= InsertMatch(ap
->a_vp
, ap
->a_uio
, &rec
,
320 keyp
, ap
->a_returnattrs
,
321 attributesBuffer
, variableBuffer
,
322 eachReturnBufferSize
, ap
->a_nummatches
);
323 if (result
== E_NONE
&& *(ap
->a_nummatches
) >= ap
->a_maxmatches
)
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 */
344 /* Unlock catalog b-tree */
345 (void) hfs_metafilelocking(VTOHFS(ap
->a_vp
), kHFSCatalogFileID
, LK_RELEASE
, p
);
347 goto ExitThisRoutine
;
348 #if 1 // Installer workaround (2940423)
351 #endif // Installer workaround
354 * Check all the catalog btree records...
355 * return the attributes for matching items
358 struct timeval myCurrentTime
;
359 struct timeval myElapsedTime
;
361 err
= BTScanNextRecord(&myBTScanState
, timerExpired
,
362 (void **)&myCurrentKeyPtr
, (void **)&myCurrentDataPtr
,
367 /* Resolve any hardlinks */
368 if (isHFSPlus
&& (ap
->a_options
& SRCHFS_SKIPLINKS
) == 0)
369 ResolveHardlink(vcb
, (HFSPlusCatalogFile
*) myCurrentDataPtr
);
371 if (CheckCriteria( vcb
, ap
->a_options
, ap
->a_searchattrs
, myCurrentDataPtr
,
372 myCurrentKeyPtr
, &searchInfo1
, &searchInfo2
, true )
373 && CheckAccess(vcb
, ap
->a_options
, myCurrentKeyPtr
, ap
->a_uio
->uio_procp
)) {
374 err
= InsertMatch(ap
->a_vp
, ap
->a_uio
, myCurrentDataPtr
,
375 myCurrentKeyPtr
, ap
->a_returnattrs
,
376 attributesBuffer
, variableBuffer
,
377 eachReturnBufferSize
, 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 myCurrentTime
= time
;
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 (VTOHFS(ap
->a_vp
)->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 static char *extension_table
=NULL
;
520 static int max_ext_width
;
523 extension_cmp(void *a
, void *b
)
525 return (strlen((char *)a
) - strlen((char *)b
));
530 // This is the api LaunchServices uses to inform the kernel
531 // the list of package extensions to ignore.
533 // Internally we keep the list sorted by the length of the
534 // the extension (from longest to shortest). We sort the
535 // list of extensions so that we can speed up our searches
536 // when comparing file names -- we only compare extensions
537 // that could possibly fit into the file name, not all of
538 // them (i.e. a short 8 character name can't have an 8
539 // character extension).
541 __private_extern__
int
542 set_package_extensions_table(void *data
, int nentries
, int maxwidth
)
544 char *new_exts
, *ptr
;
547 if (nentries
<= 0 || nentries
> 1024 || maxwidth
<= 0 || maxwidth
> 255) {
551 MALLOC(new_exts
, char *, nentries
* maxwidth
, M_TEMP
, M_WAITOK
);
553 error
= copyin(data
, new_exts
, nentries
* maxwidth
);
555 FREE(new_exts
, M_TEMP
);
559 if (extension_table
) {
560 FREE(extension_table
, M_TEMP
);
562 extension_table
= new_exts
;
564 max_ext_width
= maxwidth
;
566 qsort(extension_table
, nexts
, maxwidth
, extension_cmp
);
573 is_package_name(char *name
, int len
)
576 char *ptr
, *name_ext
;
583 for(ptr
=name
; *ptr
!= '\0'; ptr
++) {
589 // if there is no "." extension, it can't match
590 if (name_ext
== NULL
) {
594 // advance over the "."
597 // now iterate over all the extensions to see if any match
598 ptr
= &extension_table
[0];
599 for(i
=0; i
< nexts
; i
++, ptr
+=max_ext_width
) {
600 extlen
= strlen(ptr
);
601 if (strncmp(name_ext
, ptr
, extlen
) == 0 && name_ext
[extlen
] == '\0') {
607 // if we get here, no extension matched
612 // Determine if a name is "inappropriate" where the definition
613 // of "inappropriate" is up to higher level execs. Currently
614 // that's limited to /System.
617 is_inappropriate_name(char *name
, int len
)
619 char *bad_names
[] = { "System" };
620 int bad_len
[] = { 6 };
623 for(i
=0; i
< sizeof(bad_names
) / sizeof(bad_names
[0]); i
++) {
624 if (len
== bad_len
[i
] && strcmp(name
, bad_names
[i
]) == 0) {
629 // if we get here, no name matched
636 * Check to see if caller has access rights to this item
640 CheckAccess(ExtendedVCB
*theVCBPtr
, u_long searchBits
, CatalogKey
*theKeyPtr
, struct proc
*theProcPtr
)
645 HFSCatalogNodeID myNodeID
;
646 unsigned long myPerms
;
647 hfsmount_t
* my_hfsmountPtr
;
648 struct cat_desc my_cat_desc
;
649 struct cat_attr my_cat_attr
;
650 struct FndrDirInfo
*finder_info
;
653 myResult
= 0; /* default to "no access" */
654 my_cat_desc
.cd_nameptr
= NULL
;
655 my_cat_desc
.cd_namelen
= 0;
657 if ( theProcPtr
->p_ucred
->cr_uid
== 0 ) {
658 myResult
= 1; /* allow access */
659 goto ExitThisRoutine
; /* root always has access */
662 my_hfsmountPtr
= VCBTOHFS( theVCBPtr
);
663 isHFSPlus
= ( theVCBPtr
->vcbSigWord
== kHFSPlusSigWord
);
665 myNodeID
= theKeyPtr
->hfsPlus
.parentID
;
667 myNodeID
= theKeyPtr
->hfs
.parentID
;
669 while ( myNodeID
>= kRootDirID
) {
670 /* now go get catalog data for this directory */
671 myErr
= hfs_metafilelocking( my_hfsmountPtr
, kHFSCatalogFileID
, LK_SHARED
, theProcPtr
);
673 goto ExitThisRoutine
; /* no access */
675 myErr
= cat_idlookup( my_hfsmountPtr
, myNodeID
, &my_cat_desc
, &my_cat_attr
, NULL
);
676 (void) hfs_metafilelocking( my_hfsmountPtr
, kHFSCatalogFileID
, LK_RELEASE
, theProcPtr
);
678 goto ExitThisRoutine
; /* no access */
680 if (searchBits
& SRCHFS_SKIPPACKAGES
) {
681 if (is_package_name(my_cat_desc
.cd_nameptr
, my_cat_desc
.cd_namelen
)) {
683 goto ExitThisRoutine
;
687 if (searchBits
& SRCHFS_SKIPINAPPROPRIATE
) {
688 if ( my_cat_desc
.cd_parentcnid
== kRootDirID
689 && is_inappropriate_name(my_cat_desc
.cd_nameptr
, my_cat_desc
.cd_namelen
)) {
691 goto ExitThisRoutine
;
695 finder_info
= (struct FndrDirInfo
*)&my_cat_attr
.ca_finderinfo
[0];
696 if ( (searchBits
& SRCHFS_SKIPINVISIBLE
)
697 && (SWAP_BE16(finder_info
->frFlags
) & kIsInvisible
)) {
700 goto ExitThisRoutine
;
703 myNodeID
= my_cat_desc
.cd_parentcnid
; /* move up the hierarchy */
704 myPerms
= DerivePermissionSummary(my_cat_attr
.ca_uid
, my_cat_attr
.ca_gid
,
705 my_cat_attr
.ca_mode
, my_hfsmountPtr
->hfs_mp
,
706 theProcPtr
->p_ucred
, theProcPtr
);
708 cat_releasedesc( &my_cat_desc
);
710 if ( (myPerms
& X_OK
) == 0 )
711 goto ExitThisRoutine
; /* no access */
714 myResult
= 1; /* allow access */
717 cat_releasedesc( &my_cat_desc
);
723 CheckCriteria( ExtendedVCB
*vcb
,
725 struct attrlist
*attrList
,
728 searchinfospec_t
*searchInfo1
,
729 searchinfospec_t
*searchInfo2
,
732 Boolean matched
, atleastone
;
734 attrgroup_t searchAttributes
;
735 struct cat_attr c_attr
= {0};
736 struct cat_fork datafork
;
737 struct cat_fork rsrcfork
;
739 isHFSPlus
= (vcb
->vcbSigWord
== kHFSPlusSigWord
);
741 switch (rec
->recordType
) {
742 case kHFSFolderRecord
:
743 case kHFSPlusFolderRecord
:
744 if ( (searchBits
& SRCHFS_MATCHDIRS
) == 0 ) { /* If we are NOT searching folders */
751 if ( (searchBits
& SRCHFS_MATCHFILES
) == 0 ) { /* If we are NOT searching files */
757 case kHFSPlusFileRecord
:
758 /* Check if hardlink links should be skipped. */
759 if (searchBits
& SRCHFS_SKIPLINKS
) {
760 cnid_t parid
= key
->hfsPlus
.parentID
;
761 HFSPlusCatalogFile
*filep
= (HFSPlusCatalogFile
*)rec
;
763 if ((SWAP_BE32(filep
->userInfo
.fdType
) == kHardLinkFileType
) &&
764 (SWAP_BE32(filep
->userInfo
.fdCreator
) == kHFSPlusCreator
)) {
765 return (false); /* skip over link records */
766 } else if ((parid
== VCBTOHFS(vcb
)->hfs_privdir_desc
.cd_cnid
) &&
767 (filep
->bsdInfo
.special
.linkCount
== 0)) {
768 return (false); /* skip over unlinked files */
770 } else if (key
->hfsPlus
.parentID
== VCBTOHFS(vcb
)->hfs_privdir_desc
.cd_cnid
) {
771 return (false); /* skip over private files */
774 if ( (searchBits
& SRCHFS_MATCHFILES
) == 0 ) { /* If we are NOT searching files */
780 default: /* Never match a thread record or any other type. */
781 return( false ); /* Not a file or folder record, so can't search it */
784 matched
= true; /* Assume we got a match */
785 atleastone
= false; /* Dont insert unless we match at least one criteria */
787 /* First, attempt to match the name -- either partial or complete */
788 if ( attrList
->commonattr
& ATTR_CMN_NAME
) {
790 /* Check for partial/full HFS Plus name match */
792 if ( searchBits
& SRCHFS_MATCHPARTIALNAMES
) {
793 matched
= ComparePartialUnicodeName(key
->hfsPlus
.nodeName
.unicode
,
794 key
->hfsPlus
.nodeName
.length
,
795 (UniChar
*)searchInfo1
->name
,
796 searchInfo1
->nameLength
);
797 } else /* full HFS Plus name match */ {
798 matched
= (FastUnicodeCompare(key
->hfsPlus
.nodeName
.unicode
,
799 key
->hfsPlus
.nodeName
.length
,
800 (UniChar
*)searchInfo1
->name
,
801 searchInfo1
->nameLength
) == 0);
804 /* Check for partial/full HFS name match */
806 if ( searchBits
& SRCHFS_MATCHPARTIALNAMES
)
807 matched
= ComparePartialPascalName(key
->hfs
.nodeName
, (u_char
*)searchInfo1
->name
);
808 else /* full HFS name match */
809 matched
= (FastRelString(key
->hfs
.nodeName
, (u_char
*)searchInfo1
->name
) == 0);
812 #if 1 // Installer workaround (2940423)
814 HFSCatalogNodeID parentID
;
816 parentID
= key
->hfsPlus
.parentID
;
818 parentID
= key
->hfs
.parentID
;
820 if ( matched
&& parentID
== kRootDirID
&&
821 IsTargetName( searchInfo1
, isHFSPlus
) )
824 #endif // Installer workaround
826 if ( matched
== false || (searchBits
& ~SRCHFS_MATCHPARTIALNAMES
) == 0 )
827 goto TestDone
; /* no match, or nothing more to compare */
832 /* Convert catalog record into cat_attr format. */
833 cat_convertattr(VCBTOHFS(vcb
), rec
, &c_attr
, &datafork
, &rsrcfork
);
835 if (searchBits
& SRCHFS_SKIPINVISIBLE
) {
838 switch (rec
->recordType
) {
839 case kHFSFolderRecord
:
840 case kHFSPlusFolderRecord
: {
841 struct FndrDirInfo
*finder_info
;
843 finder_info
= (struct FndrDirInfo
*)&c_attr
.ca_finderinfo
[0];
844 flags
= SWAP_BE16(finder_info
->frFlags
);
849 case kHFSPlusFileRecord
: {
850 struct FndrFileInfo
*finder_info
;
852 finder_info
= (struct FndrFileInfo
*)&c_attr
.ca_finderinfo
[0];
853 flags
= SWAP_BE16(finder_info
->fdFlags
);
858 flags
= kIsInvisible
;
863 if (flags
& kIsInvisible
) {
871 /* Now that we have a record worth searching, see if it matches the search attributes */
872 if (rec
->recordType
== kHFSFileRecord
||
873 rec
->recordType
== kHFSPlusFileRecord
) {
874 if ((attrList
->fileattr
& ~ATTR_FILE_VALIDMASK
) != 0) { /* attr we do know about */
878 else if ((attrList
->fileattr
& ATTR_FILE_VALIDMASK
) != 0) {
879 searchAttributes
= attrList
->fileattr
;
881 /* File logical length (data fork) */
882 if ( searchAttributes
& ATTR_FILE_DATALENGTH
) {
883 matched
= CompareWideRange(
885 searchInfo1
->f
.dataLogicalLength
,
886 searchInfo2
->f
.dataLogicalLength
);
887 if (matched
== false) goto TestDone
;
891 /* File physical length (data fork) */
892 if ( searchAttributes
& ATTR_FILE_DATAALLOCSIZE
) {
893 matched
= CompareWideRange(
894 (u_int64_t
)datafork
.cf_blocks
* (u_int64_t
)vcb
->blockSize
,
895 searchInfo1
->f
.dataPhysicalLength
,
896 searchInfo2
->f
.dataPhysicalLength
);
897 if (matched
== false) goto TestDone
;
901 /* File logical length (resource fork) */
902 if ( searchAttributes
& ATTR_FILE_RSRCLENGTH
) {
903 matched
= CompareWideRange(
905 searchInfo1
->f
.resourceLogicalLength
,
906 searchInfo2
->f
.resourceLogicalLength
);
907 if (matched
== false) goto TestDone
;
911 /* File physical length (resource fork) */
912 if ( searchAttributes
& ATTR_FILE_RSRCALLOCSIZE
) {
913 matched
= CompareWideRange(
914 (u_int64_t
)rsrcfork
.cf_blocks
* (u_int64_t
)vcb
->blockSize
,
915 searchInfo1
->f
.resourcePhysicalLength
,
916 searchInfo2
->f
.resourcePhysicalLength
);
917 if (matched
== false) goto TestDone
;
922 atleastone
= true; /* to match SRCHFS_MATCHFILES */
926 * Check the directory attributes
928 else if (rec
->recordType
== kHFSFolderRecord
||
929 rec
->recordType
== kHFSPlusFolderRecord
) {
930 if ((attrList
->dirattr
& ~ATTR_DIR_VALIDMASK
) != 0) { /* attr we do know about */
934 else if ((attrList
->dirattr
& ATTR_DIR_VALIDMASK
) != 0) {
935 searchAttributes
= attrList
->dirattr
;
937 /* Directory valence */
938 if ( searchAttributes
& ATTR_DIR_ENTRYCOUNT
) {
939 matched
= CompareRange(c_attr
.ca_entries
,
940 searchInfo1
->d
.numFiles
,
941 searchInfo2
->d
.numFiles
);
942 if (matched
== false) goto TestDone
;
947 atleastone
= true; /* to match SRCHFS_MATCHDIRS */
952 * Check the common attributes
954 searchAttributes
= attrList
->commonattr
;
955 if ( (searchAttributes
& ATTR_CMN_VALIDMASK
) != 0 ) {
957 if ( searchAttributes
& ATTR_CMN_OBJID
) {
958 matched
= CompareRange(c_attr
.ca_fileid
,
960 searchInfo2
->nodeID
);
961 if (matched
== false) goto TestDone
;
966 if ( searchAttributes
& ATTR_CMN_PAROBJID
) {
967 HFSCatalogNodeID parentID
;
970 parentID
= key
->hfsPlus
.parentID
;
972 parentID
= key
->hfs
.parentID
;
974 matched
= CompareRange(parentID
, searchInfo1
->parentDirID
,
975 searchInfo2
->parentDirID
);
976 if (matched
== false) goto TestDone
;
980 /* Finder Info & Extended Finder Info where extFinderInfo is last 32 bytes */
981 if ( searchAttributes
& ATTR_CMN_FNDRINFO
) {
983 thisValue
= (UInt32
*) &c_attr
.ca_finderinfo
;
986 * Note: ioFlFndrInfo and ioDrUsrWds have the same offset in search info, so
987 * no need to test the object type here.
989 matched
= CompareMasked(thisValue
,
990 (UInt32
*)&searchInfo1
->finderInfo
,
991 (UInt32
*) &searchInfo2
->finderInfo
, 8);
992 if (matched
== false) goto TestDone
;
997 if ( searchAttributes
& ATTR_CMN_CRTIME
) {
998 matched
= CompareRange(c_attr
.ca_itime
,
999 searchInfo1
->creationDate
.tv_sec
,
1000 searchInfo2
->creationDate
.tv_sec
);
1001 if (matched
== false) goto TestDone
;
1006 if ( searchAttributes
& ATTR_CMN_MODTIME
) {
1007 matched
= CompareRange(c_attr
.ca_mtime
,
1008 searchInfo1
->modificationDate
.tv_sec
,
1009 searchInfo2
->modificationDate
.tv_sec
);
1010 if (matched
== false) goto TestDone
;
1015 if ( searchAttributes
& ATTR_CMN_CHGTIME
) {
1016 matched
= CompareRange(c_attr
.ca_ctime
,
1017 searchInfo1
->changeDate
.tv_sec
,
1018 searchInfo2
->changeDate
.tv_sec
);
1019 if (matched
== false) goto TestDone
;
1024 if ( searchAttributes
& ATTR_CMN_ACCTIME
) {
1025 matched
= CompareRange(c_attr
.ca_atime
,
1026 searchInfo1
->accessDate
.tv_sec
,
1027 searchInfo2
->accessDate
.tv_sec
);
1028 if (matched
== false) goto TestDone
;
1033 if ( searchAttributes
& ATTR_CMN_BKUPTIME
) {
1034 matched
= CompareRange(c_attr
.ca_btime
,
1035 searchInfo1
->lastBackupDate
.tv_sec
,
1036 searchInfo2
->lastBackupDate
.tv_sec
);
1037 if (matched
== false) goto TestDone
;
1042 if ( searchAttributes
& ATTR_CMN_OWNERID
) {
1043 matched
= CompareRange(c_attr
.ca_uid
,
1044 searchInfo1
->uid
, searchInfo2
->uid
);
1045 if (matched
== false) goto TestDone
;
1050 if ( searchAttributes
& ATTR_CMN_GRPID
) {
1051 matched
= CompareRange(c_attr
.ca_gid
,
1052 searchInfo1
->gid
, searchInfo2
->gid
);
1053 if (matched
== false) goto TestDone
;
1058 if ( searchAttributes
& ATTR_CMN_ACCESSMASK
) {
1059 matched
= CompareRange((u_long
)c_attr
.ca_mode
,
1060 (u_long
)searchInfo1
->mask
,
1061 (u_long
)searchInfo2
->mask
);
1062 if (matched
== false) goto TestDone
;
1067 /* If we got here w/o matching any, then set to false */
1073 * Finally, determine whether we need to negate the sense of the match
1074 * (i.e. find all objects that DON'T match).
1076 if ( searchBits
& SRCHFS_NEGATEPARAMS
)
1084 * Adds another record to the packed array for output
1087 InsertMatch( struct vnode
*root_vp
, struct uio
*a_uio
, CatalogRecord
*rec
,
1088 CatalogKey
*key
, struct attrlist
*returnAttrList
, void *attributesBuffer
,
1089 void *variableBuffer
, u_long bufferSize
, u_long
* nummatches
)
1092 void *rovingAttributesBuffer
;
1093 void *rovingVariableBuffer
;
1094 u_long packedBufferSize
;
1095 ExtendedVCB
*vcb
= VTOVCB(root_vp
);
1096 Boolean isHFSPlus
= vcb
->vcbSigWord
== kHFSPlusSigWord
;
1097 u_long privateDir
= VTOHFS(root_vp
)->hfs_privdir_desc
.cd_cnid
;
1098 struct attrblock attrblk
;
1099 struct cat_desc c_desc
= {0};
1100 struct cat_attr c_attr
= {0};
1101 struct cat_fork datafork
;
1102 struct cat_fork rsrcfork
;
1104 rovingAttributesBuffer
= (char*)attributesBuffer
+ sizeof(u_long
); /* Reserve space for length field */
1105 rovingVariableBuffer
= variableBuffer
;
1107 /* Convert catalog record into cat_attr format. */
1108 cat_convertattr(VTOHFS(root_vp
), rec
, &c_attr
, &datafork
, &rsrcfork
);
1110 /* hide our private meta data directory */
1111 if ((privateDir
!= 0) && (c_attr
.ca_fileid
== privateDir
)) {
1116 /* Hide the private journal files */
1117 if (VTOHFS(root_vp
)->jnl
&&
1118 ((c_attr
.ca_fileid
== VTOHFS(root_vp
)->hfs_jnlfileid
) ||
1119 (c_attr
.ca_fileid
== VTOHFS(root_vp
)->hfs_jnlinfoblkid
))) {
1124 if (returnAttrList
->commonattr
& ATTR_CMN_NAME
) {
1125 cat_convertkey(VTOHFS(root_vp
), key
, rec
, &c_desc
);
1127 c_desc
.cd_cnid
= c_attr
.ca_fileid
;
1129 c_desc
.cd_parentcnid
= key
->hfsPlus
.parentID
;
1131 c_desc
.cd_parentcnid
= key
->hfs
.parentID
;
1134 attrblk
.ab_attrlist
= returnAttrList
;
1135 attrblk
.ab_attrbufpp
= &rovingAttributesBuffer
;
1136 attrblk
.ab_varbufpp
= &rovingVariableBuffer
;
1137 attrblk
.ab_flags
= 0;
1138 attrblk
.ab_blocksize
= 0;
1140 hfs_packattrblk(&attrblk
, VTOHFS(root_vp
), NULL
, &c_desc
, &c_attr
, &datafork
, &rsrcfork
, a_uio
->uio_procp
);
1142 packedBufferSize
= (char*)rovingVariableBuffer
- (char*)attributesBuffer
;
1144 if ( packedBufferSize
> a_uio
->uio_resid
)
1145 return( errSearchBufferFull
);
1149 *((u_long
*)attributesBuffer
) = packedBufferSize
; /* Store length of fixed + var block */
1151 err
= uiomove( (caddr_t
)attributesBuffer
, packedBufferSize
, a_uio
); /* XXX should be packedBufferSize */
1153 cat_releasedesc(&c_desc
);
1160 UnpackSearchAttributeBlock( struct vnode
*vp
, struct attrlist
*alist
, searchinfospec_t
*searchInfo
, void *attributeBuffer
)
1165 DBG_ASSERT(searchInfo
!= NULL
);
1167 bufferSize
= *((u_long
*)attributeBuffer
);
1168 if (bufferSize
== 0)
1169 return (EINVAL
); /* XXX -DJB is a buffer size of zero ever valid for searchfs? */
1171 ++((u_long
*)attributeBuffer
); /* advance past the size */
1174 * UnPack common attributes
1176 a
= alist
->commonattr
;
1178 if ( a
& ATTR_CMN_NAME
) {
1179 char *s
= (char*) attributeBuffer
+ ((attrreference_t
*) attributeBuffer
)->attr_dataoffset
;
1180 size_t len
= ((attrreference_t
*) attributeBuffer
)->attr_length
;
1182 if (len
> sizeof(searchInfo
->name
))
1185 if (VTOVCB(vp
)->vcbSigWord
== kHFSPlusSigWord
) {
1187 /* Convert name to Unicode to match HFS Plus B-Tree names */
1190 if (utf8_decodestr(s
, len
-1, (UniChar
*)searchInfo
->name
, &ucslen
,
1191 sizeof(searchInfo
->name
), ':', UTF_DECOMPOSED
))
1194 searchInfo
->nameLength
= ucslen
/ sizeof(UniChar
);
1196 searchInfo
->nameLength
= 0;
1198 ++((attrreference_t
*)attributeBuffer
);
1201 /* Convert name to pascal string to match HFS B-Tree names */
1204 if (utf8_to_hfs(VTOVCB(vp
), len
-1, s
, (u_char
*)searchInfo
->name
) != 0)
1207 searchInfo
->nameLength
= searchInfo
->name
[0];
1209 searchInfo
->name
[0] = searchInfo
->nameLength
= 0;
1211 ++((attrreference_t
*)attributeBuffer
);
1214 if ( a
& ATTR_CMN_OBJID
) {
1215 searchInfo
->nodeID
= ((fsobj_id_t
*) attributeBuffer
)->fid_objno
; /* ignore fid_generation */
1216 ++((fsobj_id_t
*)attributeBuffer
);
1218 if ( a
& ATTR_CMN_PAROBJID
) {
1219 searchInfo
->parentDirID
= ((fsobj_id_t
*) attributeBuffer
)->fid_objno
; /* ignore fid_generation */
1220 ++((fsobj_id_t
*)attributeBuffer
);
1222 if ( a
& ATTR_CMN_CRTIME
) {
1223 searchInfo
->creationDate
= *((struct timespec
*)attributeBuffer
);
1224 ++((struct timespec
*)attributeBuffer
);
1226 if ( a
& ATTR_CMN_MODTIME
) {
1227 searchInfo
->modificationDate
= *((struct timespec
*)attributeBuffer
);
1228 ++((struct timespec
*)attributeBuffer
);
1230 if ( a
& ATTR_CMN_CHGTIME
) {
1231 searchInfo
->changeDate
= *((struct timespec
*)attributeBuffer
);
1232 ++((struct timespec
*)attributeBuffer
);
1234 if ( a
& ATTR_CMN_ACCTIME
) {
1235 searchInfo
->accessDate
= *((struct timespec
*)attributeBuffer
);
1236 ++((struct timespec
*)attributeBuffer
);
1238 if ( a
& ATTR_CMN_BKUPTIME
) {
1239 searchInfo
->lastBackupDate
= *((struct timespec
*)attributeBuffer
);
1240 ++((struct timespec
*)attributeBuffer
);
1242 if ( a
& ATTR_CMN_FNDRINFO
) {
1243 bcopy( attributeBuffer
, searchInfo
->finderInfo
, sizeof(u_long
) * 8 );
1244 (u_long
*)attributeBuffer
+= 8;
1246 if ( a
& ATTR_CMN_OWNERID
) {
1247 searchInfo
->uid
= *((uid_t
*)attributeBuffer
);
1248 ++((uid_t
*)attributeBuffer
);
1250 if ( a
& ATTR_CMN_GRPID
) {
1251 searchInfo
->gid
= *((gid_t
*)attributeBuffer
);
1252 ++((gid_t
*)attributeBuffer
);
1254 if ( a
& ATTR_CMN_ACCESSMASK
) {
1255 searchInfo
->mask
= *((mode_t
*)attributeBuffer
);
1256 ++((mode_t
*)attributeBuffer
);
1262 if ( a
& ATTR_DIR_ENTRYCOUNT
) {
1263 searchInfo
->d
.numFiles
= *((u_long
*)attributeBuffer
);
1264 ++((u_long
*)attributeBuffer
);
1268 a
= alist
->fileattr
;
1270 if ( a
& ATTR_FILE_DATALENGTH
) {
1271 searchInfo
->f
.dataLogicalLength
= *((off_t
*)attributeBuffer
);
1272 ++((off_t
*)attributeBuffer
);
1274 if ( a
& ATTR_FILE_DATAALLOCSIZE
) {
1275 searchInfo
->f
.dataPhysicalLength
= *((off_t
*)attributeBuffer
);
1276 ++((off_t
*)attributeBuffer
);
1278 if ( a
& ATTR_FILE_RSRCLENGTH
) {
1279 searchInfo
->f
.resourceLogicalLength
= *((off_t
*)attributeBuffer
);
1280 ++((off_t
*)attributeBuffer
);
1282 if ( a
& ATTR_FILE_RSRCALLOCSIZE
) {
1283 searchInfo
->f
.resourcePhysicalLength
= *((off_t
*)attributeBuffer
);
1284 ++((off_t
*)attributeBuffer
);
1292 #if 1 // Installer workaround (2940423)
1293 /* this routine was added as part of the work around where some installers would fail */
1294 /* because they incorrectly assumed search results were in some kind of order. */
1295 /* This routine is used to indentify the problematic target. At this point we */
1296 /* only know of one. This routine could be modified for more (I hope not). */
1297 static Boolean
IsTargetName( searchinfospec_t
* searchInfoPtr
, Boolean isHFSPlus
)
1299 if ( searchInfoPtr
->name
== NULL
)
1303 HFSUniStr255 myName
= {
1304 7, /* number of unicode characters */
1306 'L','i','b','r','a','r','y'
1309 if ( FastUnicodeCompare( myName
.unicode
, myName
.length
,
1310 (UniChar
*)searchInfoPtr
->name
,
1311 searchInfoPtr
->nameLength
) == 0 ) {
1316 u_char myName
[32] = {
1317 0x07,'L','i','b','r','a','r','y'
1319 if ( FastRelString(myName
, (u_char
*)searchInfoPtr
->name
) == 0 ) {
1325 } /* IsTargetName */
1326 #endif // Installer workaround