#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <mach/machine/vm_types.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/signalvar.h>
#include <sys/attr.h>
#include <sys/utfconv.h>
#include "hfs.h"
#include "hfs_dbg.h"
#include "hfscommon/headers/FileMgrInternal.h"
#include "hfscommon/headers/CatalogPrivate.h"
#include "hfscommon/headers/HFSUnicodeWrappers.h"
struct SearchState {
long searchBits;
BTreeIterator btreeIterator;
};
typedef struct SearchState SearchState;
static int UnpackSearchAttributeBlock(struct vnode *vp, struct attrlist *alist, searchinfospec_t *searchInfo, void *attributeBuffer);
Boolean CheckCriteria(ExtendedVCB *vcb, const SearchState *searchState,
u_long searchBits, struct attrlist *attrList,
CatalogNodeData *cnp, CatalogKey *key,
searchinfospec_t *searchInfo1, searchinfospec_t *searchInfo2);
static int CheckAccess(CatalogNodeData *cnp, CatalogKey *key, struct proc *p);
static int InsertMatch(struct vnode *vp, struct uio *a_uio, CatalogNodeData *cnp,
CatalogKey *key, struct attrlist *returnAttrList,
void *attributesBuffer, void *variableBuffer,
u_long bufferSize, u_long * nummatches );
static Boolean CompareRange(u_long val, u_long low, u_long high);
static Boolean CompareWideRange(u_int64_t val, u_int64_t low, u_int64_t high);
static Boolean CompareRange( u_long val, u_long low, u_long high )
{
return( (val >= low) && (val <= high) );
}
static Boolean CompareWideRange( u_int64_t val, u_int64_t low, u_int64_t high )
{
return( (val >= low) && (val <= high) );
}
#define errSearchBufferFull 101
int
hfs_search( ap )
struct vop_searchfs_args *ap;
{
CatalogNodeData cnode;
BTreeKey *key;
FSBufferDescriptor btRecord;
FCB* catalogFCB;
SearchState *searchState;
searchinfospec_t searchInfo1;
searchinfospec_t searchInfo2;
void *attributesBuffer;
void *variableBuffer;
short recordSize;
short operation;
u_long fixedBlockSize;
u_long eachReturnBufferSize;
struct proc *p = current_proc();
u_long nodesToCheck = 30;
u_long lastNodeNum = 0XFFFFFFFF;
ExtendedVCB *vcb = VTOVCB(ap->a_vp);
int err = E_NONE;
int isHFSPlus;
*(ap->a_nummatches) = 0;
if ( ap->a_options & ~SRCHFS_VALIDOPTIONSMASK )
return( EINVAL );
if (ap->a_uio->uio_resid <= 0)
return (EINVAL);
isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
searchState = (SearchState *)ap->a_searchstate;
if ( ap->a_options & SRCHFS_START ) {
bzero( (caddr_t)searchState, sizeof(SearchState) );
operation = kBTreeFirstRecord;
ap->a_options &= ~SRCHFS_START;
} else {
operation = kBTreeCurrentRecord;
}
err = UnpackSearchAttributeBlock( ap->a_vp, ap->a_searchattrs, &searchInfo1, ap->a_searchparams1 );
if (err) return err;
err = UnpackSearchAttributeBlock( ap->a_vp, ap->a_searchattrs, &searchInfo2, ap->a_searchparams2 );
if (err) return err;
btRecord.itemCount = 1;
if (isHFSPlus) {
btRecord.itemSize = sizeof(cnode);
btRecord.bufferAddress = &cnode;
} else {
btRecord.itemSize = sizeof(HFSCatalogFile);
btRecord.bufferAddress = &cnode.cnd_extra;
}
catalogFCB = VTOFCB( vcb->catalogRefNum );
key = (BTreeKey*) &(searchState->btreeIterator.key);
fixedBlockSize = sizeof(u_long) + AttributeBlockSize( ap->a_returnattrs );
eachReturnBufferSize = fixedBlockSize;
if ( ap->a_returnattrs->commonattr & ATTR_CMN_NAME )
eachReturnBufferSize += kHFSPlusMaxFileNameBytes + 1;
MALLOC( attributesBuffer, void *, eachReturnBufferSize, M_TEMP, M_WAITOK );
variableBuffer = (void*)((char*) attributesBuffer + fixedBlockSize);
err = hfs_metafilelocking( VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_SHARED, p );
if ( err != E_NONE ) {
goto ExitThisRoutine;
};
err = BTIterateRecord( catalogFCB, operation, &(searchState->btreeIterator), &btRecord, &recordSize );
while( err == E_NONE ) {
if (!isHFSPlus)
CopyCatalogNodeData(vcb, (CatalogRecord*)&cnode.cnd_extra, &cnode);
if ( CheckCriteria( vcb, searchState, ap->a_options, ap->a_searchattrs, &cnode,
(CatalogKey *)key, &searchInfo1, &searchInfo2 ) &&
CheckAccess(&cnode, (CatalogKey *)key, ap->a_uio->uio_procp)) {
err = InsertMatch(ap->a_vp, ap->a_uio, &cnode, (CatalogKey *)key,
ap->a_returnattrs, attributesBuffer, variableBuffer,
eachReturnBufferSize, ap->a_nummatches);
if ( err != E_NONE )
break;
}
err = BTIterateRecord( catalogFCB, kBTreeNextRecord, &(searchState->btreeIterator), &btRecord, &recordSize );
if ( *(ap->a_nummatches) >= ap->a_maxmatches )
break;
if ( searchState->btreeIterator.hint.nodeNum != lastNodeNum ) {
lastNodeNum = searchState->btreeIterator.hint.nodeNum;
if ( --nodesToCheck == 0 )
break;
}
}
(void) hfs_metafilelocking( VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_RELEASE, p );
if ( err == E_NONE ) {
err = EAGAIN;
} else if ( err == errSearchBufferFull ) {
if ( *(ap->a_nummatches) > 0 )
err = EAGAIN;
else
err = ENOBUFS;
} else if ( err == btNotFound ) {
err = E_NONE;
}
ExitThisRoutine:
FREE( attributesBuffer, M_TEMP );
return( err );
}
static Boolean
CompareMasked(const UInt32 *thisValue, const UInt32 *compareData,
const UInt32 *compareMask, UInt32 count)
{
Boolean matched;
UInt32 i;
matched = true;
for (i=0; i<count; i++) {
if (((*thisValue++ ^ *compareData++) & *compareMask++) != 0) {
matched = false;
break;
}
}
return matched;
}
static Boolean
ComparePartialUnicodeName (register ConstUniCharArrayPtr str, register ItemCount s_len,
register ConstUniCharArrayPtr find, register ItemCount f_len )
{
if (f_len == 0 || s_len == 0)
return FALSE;
do {
if (s_len-- < f_len)
return FALSE;
} while (FastUnicodeCompare(str++, f_len, find, f_len) != 0);
return TRUE;
}
static Boolean
ComparePartialPascalName ( register ConstStr31Param str, register ConstStr31Param find )
{
register u_char s_len = str[0];
register u_char f_len = find[0];
register u_char *tsp;
Str31 tmpstr;
if (f_len == 0 || s_len == 0)
return FALSE;
bcopy(str, tmpstr, s_len + 1);
tsp = &tmpstr[0];
while (s_len-- >= f_len) {
*tsp = f_len;
if (FastRelString(tsp++, find) == 0)
return TRUE;
}
return FALSE;
}
static int
CheckAccess(CatalogNodeData *cnp, CatalogKey *key, struct proc *p)
{
return (1);
}
Boolean
CheckCriteria( ExtendedVCB *vcb, const SearchState *searchState, u_long searchBits,
struct attrlist *attrList, CatalogNodeData *cnp, CatalogKey *key,
searchinfospec_t *searchInfo1, searchinfospec_t *searchInfo2 )
{
Boolean matched, atleastone;
Boolean isHFSPlus;
attrgroup_t searchAttributes;
isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
switch (cnp->cnd_type) {
case kCatalogFolderNode:
if ( (searchBits & SRCHFS_MATCHDIRS) == 0 ) {
matched = false;
goto TestDone;
}
break;
case kCatalogFileNode:
if ( (searchBits & SRCHFS_MATCHFILES) == 0 ) {
matched = false;
goto TestDone;
}
break;
default:
return( false );
}
matched = true;
atleastone = false;
if ( attrList->commonattr & ATTR_CMN_NAME ) {
if (isHFSPlus) {
if ( searchBits & SRCHFS_MATCHPARTIALNAMES ) {
matched = ComparePartialUnicodeName(key->hfsPlus.nodeName.unicode,
key->hfsPlus.nodeName.length,
(UniChar*)searchInfo1->name,
searchInfo1->nameLength );
} else {
matched = (FastUnicodeCompare(key->hfsPlus.nodeName.unicode,
key->hfsPlus.nodeName.length,
(UniChar*)searchInfo1->name,
searchInfo1->nameLength ) == 0);
}
} else {
if ( searchBits & SRCHFS_MATCHPARTIALNAMES )
matched = ComparePartialPascalName(key->hfs.nodeName, (u_char*)searchInfo1->name);
else
matched = (FastRelString(key->hfs.nodeName, (u_char*)searchInfo1->name) == 0);
}
if ( matched == false || (searchBits & ~SRCHFS_MATCHPARTIALNAMES) == 0 )
goto TestDone;
atleastone = true;
}
if (cnp->cnd_type == kCatalogFileNode) {
if ((attrList->dirattr & ~ATTR_FILE_VALIDMASK) != 0) {
matched = false;
goto TestDone;
}
else if ((attrList->dirattr & ATTR_FILE_VALIDMASK) != 0) {
searchAttributes = attrList->fileattr;
if ( searchAttributes & ATTR_FILE_DATALENGTH ) {
matched = CompareWideRange(
cnp->cnd_datafork.logicalSize,
searchInfo1->f.dataLogicalLength,
searchInfo2->f.dataLogicalLength);
if (matched == false) goto TestDone;
atleastone = true;
}
if ( searchAttributes & ATTR_FILE_DATAALLOCSIZE ) {
matched = CompareWideRange(
cnp->cnd_datafork.totalBlocks * vcb->blockSize,
searchInfo1->f.dataPhysicalLength,
searchInfo2->f.dataPhysicalLength);
if (matched == false) goto TestDone;
atleastone = true;
}
if ( searchAttributes & ATTR_FILE_RSRCLENGTH ) {
matched = CompareWideRange(
cnp->cnd_rsrcfork.logicalSize,
searchInfo1->f.resourceLogicalLength,
searchInfo2->f.resourceLogicalLength);
if (matched == false) goto TestDone;
atleastone = true;
}
if ( searchAttributes & ATTR_FILE_RSRCALLOCSIZE ) {
matched = CompareWideRange(
cnp->cnd_rsrcfork.totalBlocks * vcb->blockSize,
searchInfo1->f.resourcePhysicalLength,
searchInfo2->f.resourcePhysicalLength);
if (matched == false) goto TestDone;
atleastone = true;
}
}
else {
atleastone = true;
}
}
else if (cnp->cnd_type == kCatalogFolderNode) {
if ((attrList->dirattr & ~ATTR_DIR_VALIDMASK) != 0) {
matched = false;
goto TestDone;
}
else if ((attrList->dirattr & ATTR_DIR_VALIDMASK) != 0) {
searchAttributes = attrList->dirattr;
if ( searchAttributes & ATTR_DIR_ENTRYCOUNT ) {
matched = CompareRange(cnp->cnd_valence, searchInfo1->d.numFiles, searchInfo2->d.numFiles );
if (matched == false) goto TestDone;
atleastone = true;
}
}
else {
atleastone = true;
}
}
searchAttributes = attrList->commonattr;
if ( (searchAttributes & ATTR_CMN_VALIDMASK) != 0 ) {
if ( searchAttributes & ATTR_CMN_OBJID ) {
matched = CompareRange( cnp->cnd_nodeID, searchInfo1->nodeID, searchInfo2->nodeID );
if (matched == false) goto TestDone;
atleastone = true;
}
if ( searchAttributes & ATTR_CMN_PAROBJID ) {
HFSCatalogNodeID parentID;
if (isHFSPlus)
parentID = key->hfsPlus.parentID;
else
parentID = key->hfs.parentID;
matched = CompareRange( parentID, searchInfo1->parentDirID, searchInfo2->parentDirID );
if (matched == false) goto TestDone;
atleastone = true;
}
if ( searchAttributes & ATTR_CMN_FNDRINFO ) {
UInt32 *thisValue;
thisValue = (UInt32 *) &cnp->cnd_finderInfo;
matched = CompareMasked( thisValue, (UInt32 *) &searchInfo1->finderInfo,
(UInt32 *) &searchInfo2->finderInfo, 8 );
if (matched == false) goto TestDone;
atleastone = true;
}
if ( searchAttributes & ATTR_CMN_CRTIME ) {
matched = CompareRange(to_bsd_time(cnp->cnd_createDate),
searchInfo1->creationDate.tv_sec, searchInfo2->creationDate.tv_sec );
if (matched == false) goto TestDone;
atleastone = true;
}
if ( searchAttributes & ATTR_CMN_MODTIME ) {
matched = CompareRange(to_bsd_time(cnp->cnd_contentModDate),
searchInfo1->modificationDate.tv_sec, searchInfo2->modificationDate.tv_sec );
if (matched == false) goto TestDone;
atleastone = true;
}
if ( searchAttributes & ATTR_CMN_CHGTIME ) {
matched = CompareRange(to_bsd_time(cnp->cnd_attributeModDate),
searchInfo1->changeDate.tv_sec, searchInfo2->changeDate.tv_sec );
if (matched == false) goto TestDone;
atleastone = true;
}
if ( searchAttributes & ATTR_CMN_BKUPTIME ) {
matched = CompareRange(to_bsd_time(cnp->cnd_backupDate),
searchInfo1->lastBackupDate.tv_sec, searchInfo2->lastBackupDate.tv_sec );
if (matched == false) goto TestDone;
atleastone = true;
}
if ( searchAttributes & ATTR_CMN_OWNERID ) {
matched = CompareRange( cnp->cnd_ownerID, searchInfo1->uid, searchInfo2->uid );
if (matched == false) goto TestDone;
atleastone = true;
}
if ( searchAttributes & ATTR_CMN_GRPID ) {
matched = CompareRange( cnp->cnd_groupID, searchInfo1->gid, searchInfo2->gid );
if (matched == false) goto TestDone;
atleastone = true;
}
if ( searchAttributes & ATTR_CMN_ACCESSMASK ) {
matched = CompareRange( (u_long)cnp->cnd_mode,
(u_long)searchInfo1->mask, (u_long)searchInfo2->mask );
if (matched == false) goto TestDone;
atleastone = true;
}
}
if (! atleastone)
matched = false;
TestDone:
if ( searchBits & SRCHFS_NEGATEPARAMS )
matched = !matched;
return( matched );
}
static int
InsertMatch( struct vnode *root_vp, struct uio *a_uio, CatalogNodeData *cnp,
CatalogKey *key, struct attrlist *returnAttrList, void *attributesBuffer,
void *variableBuffer, u_long bufferSize, u_long * nummatches )
{
int err;
void *rovingAttributesBuffer;
void *rovingVariableBuffer;
struct hfsCatalogInfo catalogInfo;
u_long packedBufferSize;
ExtendedVCB *vcb = VTOVCB(root_vp);
Boolean isHFSPlus = vcb->vcbSigWord == kHFSPlusSigWord;
u_long privateDir = VTOHFS(root_vp)->hfs_private_metadata_dir;
rovingAttributesBuffer = (char*)attributesBuffer + sizeof(u_long);
rovingVariableBuffer = variableBuffer;
INIT_CATALOGDATA(&catalogInfo.nodeData, 0);
catalogInfo.nodeData.cnd_iNodeNumCopy = 0;
bcopy(cnp, &catalogInfo.nodeData, (cnp->cnd_type == kCatalogFileNode) ?
sizeof(HFSPlusCatalogFile) : sizeof(HFSPlusCatalogFolder));
catalogInfo.nodeData.cnm_parID = isHFSPlus ? key->hfsPlus.parentID : key->hfs.parentID;
if ((privateDir != 0) && (catalogInfo.nodeData.cnm_parID == privateDir))
return (0);
if ((privateDir != 0) && (catalogInfo.nodeData.cnd_nodeID == privateDir))
return (0);
if ( returnAttrList->commonattr & ATTR_CMN_NAME ) {
size_t utf8len = 0;
catalogInfo.nodeData.cnm_nameptr = catalogInfo.nodeData.cnm_namespace;
if ( isHFSPlus ) {
err = utf8_encodestr(key->hfsPlus.nodeName.unicode,
key->hfsPlus.nodeName.length * sizeof(UniChar),
catalogInfo.nodeData.cnm_namespace,
&utf8len,
MAXHFSVNODELEN + 1, ':', 0);
if (err == ENAMETOOLONG) {
utf8len = utf8_encodelen(key->hfsPlus.nodeName.unicode,
key->hfsPlus.nodeName.length * sizeof(UniChar), ':', 0);
MALLOC(catalogInfo.nodeData.cnm_nameptr, char *, utf8len+1, M_TEMP, M_WAITOK);
catalogInfo.nodeData.cnm_flags |= kCatNameIsAllocated;
err = utf8_encodestr(key->hfsPlus.nodeName.unicode,
key->hfsPlus.nodeName.length * sizeof(UniChar),
catalogInfo.nodeData.cnm_nameptr,
&utf8len,
utf8len + 1, ':', 0);
}
} else {
err = hfs_to_utf8(vcb,
key->hfs.nodeName,
MAXHFSVNODELEN + 1,
(ByteCount*) &utf8len,
catalogInfo.nodeData.cnm_namespace);
if (err == ENAMETOOLONG) {
MALLOC(catalogInfo.nodeData.cnm_nameptr, char *, utf8len+1, M_TEMP, M_WAITOK);
catalogInfo.nodeData.cnm_flags |= kCatNameIsAllocated;
err = hfs_to_utf8(vcb,
key->hfs.nodeName,
utf8len + 1,
(ByteCount*) &utf8len,
catalogInfo.nodeData.cnm_nameptr);
} else if (err) {
err = mac_roman_to_utf8(key->hfs.nodeName, MAXHFSVNODELEN + 1,
(ByteCount*) &utf8len,
catalogInfo.nodeData.cnm_namespace);
}
}
catalogInfo.nodeData.cnm_length = utf8len;
if (err && (catalogInfo.nodeData.cnm_flags & kCatNameIsAllocated))
{
DisposePtr(catalogInfo.nodeData.cnm_nameptr);
catalogInfo.nodeData.cnm_flags &= ~kCatNameIsAllocated;
catalogInfo.nodeData.cnm_nameptr = catalogInfo.nodeData.cnm_namespace;
catalogInfo.nodeData.cnm_namespace[0] = 0;
}
}
PackCatalogInfoAttributeBlock( returnAttrList,root_vp, &catalogInfo, &rovingAttributesBuffer, &rovingVariableBuffer );
CLEAN_CATALOGDATA(&catalogInfo.nodeData);
packedBufferSize = (char*)rovingVariableBuffer - (char*)attributesBuffer;
if ( packedBufferSize > a_uio->uio_resid )
return( errSearchBufferFull );
(* nummatches)++;
*((u_long *)attributesBuffer) = packedBufferSize;
err = uiomove( (caddr_t)attributesBuffer, packedBufferSize, a_uio );
return( err );
}
static int
UnpackSearchAttributeBlock( struct vnode *vp, struct attrlist *alist, searchinfospec_t *searchInfo, void *attributeBuffer )
{
attrgroup_t a;
u_long bufferSize;
DBG_ASSERT(searchInfo != NULL);
bufferSize = *((u_long *)attributeBuffer);
if (bufferSize == 0)
return (EINVAL);
++((u_long *)attributeBuffer);
a = alist->commonattr;
if ( a != 0 ) {
if ( a & ATTR_CMN_NAME ) {
char *s = (char*) attributeBuffer + ((attrreference_t *) attributeBuffer)->attr_dataoffset;
size_t len = ((attrreference_t *) attributeBuffer)->attr_length;
if (len > sizeof(searchInfo->name))
return (EINVAL);
if (VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) {
size_t ucslen;
if (len > 0) {
if (utf8_decodestr(s, len-1, (UniChar*)searchInfo->name, &ucslen,
sizeof(searchInfo->name), ':', UTF_DECOMPOSED))
return (EINVAL);
searchInfo->nameLength = ucslen / sizeof(UniChar);
} else {
searchInfo->nameLength = 0;
}
++((attrreference_t *)attributeBuffer);
} else {
if (len > 0) {
if (utf8_to_hfs(VTOVCB(vp), len-1, s, (u_char*)searchInfo->name) != 0)
return (EINVAL);
searchInfo->nameLength = searchInfo->name[0];
} else {
searchInfo->name[0] = searchInfo->nameLength = 0;
}
++((attrreference_t *)attributeBuffer);
}
}
if ( a & ATTR_CMN_OBJID ) {
searchInfo->nodeID = ((fsobj_id_t *) attributeBuffer)->fid_objno;
++((fsobj_id_t *)attributeBuffer);
}
if ( a & ATTR_CMN_PAROBJID ) {
searchInfo->parentDirID = ((fsobj_id_t *) attributeBuffer)->fid_objno;
++((fsobj_id_t *)attributeBuffer);
}
if ( a & ATTR_CMN_CRTIME ) {
searchInfo->creationDate = *((struct timespec *)attributeBuffer);
++((struct timespec *)attributeBuffer);
}
if ( a & ATTR_CMN_MODTIME ) {
searchInfo->modificationDate = *((struct timespec *)attributeBuffer);
++((struct timespec *)attributeBuffer);
}
if ( a & ATTR_CMN_CHGTIME ) {
searchInfo->changeDate = *((struct timespec *)attributeBuffer);
++((struct timespec *)attributeBuffer);
}
if ( a & ATTR_CMN_BKUPTIME ) {
searchInfo->lastBackupDate = *((struct timespec *)attributeBuffer);
++((struct timespec *)attributeBuffer);
}
if ( a & ATTR_CMN_FNDRINFO ) {
bcopy( attributeBuffer, searchInfo->finderInfo, sizeof(u_long) * 8 );
(u_long *)attributeBuffer += 8;
}
if ( a & ATTR_CMN_BKUPTIME ) {
searchInfo->lastBackupDate = *((struct timespec *)attributeBuffer);
++((struct timespec *)attributeBuffer);
}
if ( a & ATTR_CMN_OWNERID ) {
searchInfo->uid = *((uid_t *)attributeBuffer);
++((uid_t *)attributeBuffer);
}
if ( a & ATTR_CMN_GRPID ) {
searchInfo->gid = *((gid_t *)attributeBuffer);
++((gid_t *)attributeBuffer);
}
if ( a & ATTR_CMN_ACCESSMASK ) {
searchInfo->mask = *((mode_t *)attributeBuffer);
++((mode_t *)attributeBuffer);
}
}
a = alist->dirattr;
if ( a != 0 ) {
if ( a & ATTR_DIR_ENTRYCOUNT ) {
searchInfo->d.numFiles = *((u_long *)attributeBuffer);
++((u_long *)attributeBuffer);
}
}
a = alist->fileattr;
if ( a != 0 ) {
if ( a & ATTR_FILE_DATALENGTH ) {
searchInfo->f.dataLogicalLength = *((off_t *)attributeBuffer);
++((off_t *)attributeBuffer);
}
if ( a & ATTR_FILE_DATAALLOCSIZE ) {
searchInfo->f.dataPhysicalLength = *((off_t *)attributeBuffer);
++((off_t *)attributeBuffer);
}
if ( a & ATTR_FILE_RSRCLENGTH ) {
searchInfo->f.resourceLogicalLength = *((off_t *)attributeBuffer);
++((off_t *)attributeBuffer);
}
if ( a & ATTR_FILE_RSRCALLOCSIZE ) {
searchInfo->f.resourcePhysicalLength = *((off_t *)attributeBuffer);
++((off_t *)attributeBuffer);
}
}
return (0);
}