]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/hfs/hfs_search.c
xnu-517.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_search.c
index 0c7638fbe86a4f05fdec7b2cf8d7e8e9b16da248..008f78fe0c97a30c327fa1fccfd205bc6f5fe5f7 100644 (file)
@@ -1,21 +1,24 @@
 /*
- * Copyright (c) 1997-2002 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1997-2003 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
  * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
  * 
  * @APPLE_LICENSE_HEADER_END@
  *
@@ -99,7 +102,7 @@ static int CheckCriteria(    ExtendedVCB *vcb,
                                                        searchinfospec_t *searchInfo2,
                                                        Boolean lookForDup );
 
-static int CheckAccess(ExtendedVCB *vcb, CatalogKey *key, struct proc *p);
+static int CheckAccess(ExtendedVCB *vcb, u_long searchBits, CatalogKey *key, struct proc *p);
 
 static int InsertMatch(struct vnode *vp, struct uio *a_uio, CatalogRecord *rec,
                        CatalogKey *key, struct attrlist *returnAttrList,
@@ -158,6 +161,7 @@ vop_searchfs {
 };
 */
 
+__private_extern__
 int
 hfs_search( ap )
        struct vop_searchfs_args *ap; /*
@@ -193,6 +197,9 @@ hfs_search( ap )
        CatalogRecord * myCurrentDataPtr;
        CatPosition * myCatPositionPtr;
        BTScanState myBTScanState;
+       void *user_start = NULL;
+       int   user_len;
+       int32_t searchTime;
 
        /* XXX Parameter check a_searchattrs? */
 
@@ -201,10 +208,32 @@ hfs_search( ap )
        if (ap->a_options & ~SRCHFS_VALIDOPTIONSMASK)
                return (EINVAL);
 
+       /* SRCHFS_SKIPLINKS requires root access.
+        * This option cannot be used with either
+        * the ATTR_CMN_NAME or ATTR_CMN_PAROBJID
+        * attributes.
+        */
+       if (ap->a_options & SRCHFS_SKIPLINKS) {
+               attrgroup_t attrs;
+
+               attrs = ap->a_searchattrs->commonattr | ap->a_returnattrs->commonattr;
+               if (attrs & (ATTR_CMN_NAME | ATTR_CMN_PAROBJID))
+                       return (EINVAL);
+               if ((err = suser(p->p_ucred, &p->p_acflag)))
+                       return (err);
+       }
+
        if (ap->a_uio->uio_resid <= 0)
                return (EINVAL);
 
        isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
+       
+       searchTime = kMaxMicroSecsInKernel;
+       if (ap->a_timelimit->tv_sec == 0 &&
+           ap->a_timelimit->tv_usec > 0 &&
+           ap->a_timelimit->tv_usec < kMaxMicroSecsInKernel) {
+               searchTime = ap->a_timelimit->tv_usec;
+       }
 
        /* UnPack the search boundries, searchInfo1, searchInfo2 */
        err = UnpackSearchAttributeBlock(ap->a_vp, ap->a_searchattrs,
@@ -223,6 +252,20 @@ hfs_search( ap )
        MALLOC( attributesBuffer, void *, eachReturnBufferSize, M_TEMP, M_WAITOK );
        variableBuffer = (void*)((char*) attributesBuffer + fixedBlockSize);
 
+       // XXXdbg - have to lock the user's buffer so we don't fault
+       // while holding the shared catalog file lock.  see the comment
+       // in hfs_readdir() for more details.
+       //
+       if (VTOHFS(ap->a_vp)->jnl && ap->a_uio->uio_segflg == UIO_USERSPACE) {
+               user_start = ap->a_uio->uio_iov->iov_base;
+               user_len   = ap->a_uio->uio_iov->iov_len;
+
+               if ((err = vslock(user_start, user_len)) != 0) {
+                       user_start = NULL;
+                       goto ExitThisRoutine;
+               }
+       }
+
        /* Lock catalog b-tree */
        err = hfs_metafilelocking(VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_SHARED, p);
        if (err)
@@ -237,6 +280,10 @@ hfs_search( ap )
                /* Starting a new search. */
                /* Make sure the on-disk Catalog file is current */
                (void) VOP_FSYNC(vcb->catalogRefNum, NOCRED, MNT_WAIT, p);
+               if (VTOHFS(ap->a_vp)->jnl) {
+                   journal_flush(VTOHFS(ap->a_vp)->jnl);
+               }
+
                ap->a_options &= ~SRCHFS_START;
                bzero( (caddr_t)myCatPositionPtr, sizeof( *myCatPositionPtr ) );
                err = BTScanInitialize(catalogFCB, 0, 0, 0, kCatSearchBufferSize, &myBTScanState);
@@ -270,7 +317,7 @@ hfs_search( ap )
                        if ( result == E_NONE ) {
                                if (CheckCriteria(vcb, ap->a_options, ap->a_searchattrs, &rec,
                                                                  keyp, &searchInfo1, &searchInfo2, false) &&
-                                       CheckAccess(vcb, keyp, ap->a_uio->uio_procp)) {
+                                       CheckAccess(vcb, ap->a_options, keyp, ap->a_uio->uio_procp)) {
                
                                        result = InsertMatch(ap->a_vp, ap->a_uio, &rec, 
                                                                          keyp, ap->a_returnattrs,
@@ -321,12 +368,12 @@ hfs_search( ap )
                        break;
 
                /* Resolve any hardlinks */
-               if (isHFSPlus)
+               if (isHFSPlus && (ap->a_options & SRCHFS_SKIPLINKS) == 0)
                        ResolveHardlink(vcb, (HFSPlusCatalogFile *) myCurrentDataPtr);
 
                if (CheckCriteria( vcb, ap->a_options, ap->a_searchattrs, myCurrentDataPtr,
                                myCurrentKeyPtr, &searchInfo1, &searchInfo2, true )
-               &&  CheckAccess(vcb, myCurrentKeyPtr, ap->a_uio->uio_procp)) {
+               &&  CheckAccess(vcb, ap->a_options, myCurrentKeyPtr, ap->a_uio->uio_procp)) {
                        err = InsertMatch(ap->a_vp, ap->a_uio, myCurrentDataPtr, 
                                        myCurrentKeyPtr, ap->a_returnattrs,
                                        attributesBuffer, variableBuffer,
@@ -354,7 +401,7 @@ hfs_search( ap )
                timersub(&myCurrentTime, &myBTScanState.startTime, &myElapsedTime);
                /* Note: assumes kMaxMicroSecsInKernel is less than 1,000,000 */
                if (myElapsedTime.tv_sec > 0
-               ||  myElapsedTime.tv_usec >= kMaxMicroSecsInKernel) {
+               ||  myElapsedTime.tv_usec >= searchTime) {
                        timerExpired = true;
                }
        }
@@ -383,6 +430,10 @@ QuickExit:
 ExitThisRoutine:
         FREE( attributesBuffer, M_TEMP );
 
+       if (VTOHFS(ap->a_vp)->jnl && user_start) {
+               vsunlock(user_start, user_len, TRUE);
+       }
+
        return (MacToVFSError(err));
 }
 
@@ -395,7 +446,12 @@ ResolveHardlink(ExtendedVCB *vcb, HFSPlusCatalogFile *recp)
        &&  (SWAP_BE32(recp->userInfo.fdCreator) == kHFSPlusCreator)
        &&  ((to_bsd_time(recp->createDate) == vcb->vcbCrDate) ||
             (to_bsd_time(recp->createDate) == VCBTOHFS(vcb)->hfs_metadata_createdate))) {
+               cnid_t saved_cnid;
+
+               /* Export link's cnid (a unique value) instead of inode's cnid */
+               saved_cnid = recp->fileID;
                (void) resolvelink(VCBTOHFS(vcb), recp->bsdInfo.special.iNodeNum, recp);
+               recp->fileID = saved_cnid;
        }
 }
 
@@ -461,12 +517,130 @@ ComparePartialPascalName ( register ConstStr31Param str, register ConstStr31Para
 }
 
 
+
+static char *extension_table=NULL;
+static int   nexts;
+static int   max_ext_width;
+
+static int
+extension_cmp(void *a, void *b)
+{
+    return (strlen((char *)a) - strlen((char *)b));
+}
+
+
+//
+// This is the api LaunchServices uses to inform the kernel
+// the list of package extensions to ignore.
+//
+// Internally we keep the list sorted by the length of the
+// the extension (from longest to shortest).  We sort the
+// list of extensions so that we can speed up our searches
+// when comparing file names -- we only compare extensions
+// that could possibly fit into the file name, not all of
+// them (i.e. a short 8 character name can't have an 8
+// character extension).
+//
+__private_extern__ int
+set_package_extensions_table(void *data, int nentries, int maxwidth)
+{
+    char *new_exts, *ptr;
+    int error, i, len;
+    
+    if (nentries <= 0 || nentries > 1024 || maxwidth <= 0 || maxwidth > 255) {
+       return EINVAL;
+    }
+
+    MALLOC(new_exts, char *, nentries * maxwidth, M_TEMP, M_WAITOK);
+    
+    error = copyin(data, new_exts, nentries * maxwidth);
+    if (error) {
+       FREE(new_exts, M_TEMP);
+       return error;
+    }
+
+    if (extension_table) {
+       FREE(extension_table, M_TEMP);
+    }
+    extension_table = new_exts;
+    nexts           = nentries;
+    max_ext_width   = maxwidth;
+
+    qsort(extension_table, nexts, maxwidth, extension_cmp);
+
+    return 0;
+}
+
+
+static int
+is_package_name(char *name, int len)
+{
+    int i, extlen;
+    char *ptr, *name_ext;
+    
+    if (len <= 3) {
+       return 0;
+    }
+
+    name_ext = NULL;
+    for(ptr=name; *ptr != '\0'; ptr++) {
+       if (*ptr == '.') {
+           name_ext = ptr;
+       }
+    }
+
+    // if there is no "." extension, it can't match
+    if (name_ext == NULL) {
+       return 0;
+    }
+
+    // advance over the "."
+    name_ext++;
+
+    // now iterate over all the extensions to see if any match
+    ptr = &extension_table[0];
+    for(i=0; i < nexts; i++, ptr+=max_ext_width) {
+       extlen = strlen(ptr);
+       if (strncmp(name_ext, ptr, extlen) == 0 && name_ext[extlen] == '\0') {
+           // aha, a match!
+           return 1;
+       }
+    }
+
+    // if we get here, no extension matched
+    return 0;
+}
+
+//
+// Determine if a name is "inappropriate" where the definition
+// of "inappropriate" is up to higher level execs.  Currently
+// that's limited to /System.
+//
+static int
+is_inappropriate_name(char *name, int len)
+{
+    char *bad_names[] = { "System" };
+    int   bad_len[]   = {        6 };
+    int  i;
+    
+    for(i=0; i < sizeof(bad_names) / sizeof(bad_names[0]); i++) {
+       if (len == bad_len[i] && strcmp(name, bad_names[i]) == 0) {
+           return 1;
+       }
+    }
+
+    // if we get here, no name matched
+    return 0;
+}
+
+
+
 /*
  * Check to see if caller has access rights to this item
  */
 
 static int
-CheckAccess(ExtendedVCB *theVCBPtr, CatalogKey *theKeyPtr, struct proc *theProcPtr)
+CheckAccess(ExtendedVCB *theVCBPtr, u_long searchBits, CatalogKey *theKeyPtr, struct proc *theProcPtr)
 {
        Boolean                 isHFSPlus;
        int                     myErr;
@@ -476,6 +650,8 @@ CheckAccess(ExtendedVCB *theVCBPtr, CatalogKey *theKeyPtr, struct proc *theProcP
        hfsmount_t *            my_hfsmountPtr;
        struct cat_desc         my_cat_desc;
        struct cat_attr         my_cat_attr;
+       struct FndrDirInfo     *finder_info;
+
        
        myResult = 0;   /* default to "no access" */
        my_cat_desc.cd_nameptr = NULL;
@@ -504,10 +680,34 @@ CheckAccess(ExtendedVCB *theVCBPtr, CatalogKey *theKeyPtr, struct proc *theProcP
                if ( myErr )
                        goto ExitThisRoutine;   /* no access */
 
+               if (searchBits & SRCHFS_SKIPPACKAGES) {
+                   if (is_package_name(my_cat_desc.cd_nameptr, my_cat_desc.cd_namelen)) {
+                       myResult = 0;
+                       goto ExitThisRoutine;
+                   }
+               }
+
+               if (searchBits & SRCHFS_SKIPINAPPROPRIATE) {
+                   if (   my_cat_desc.cd_parentcnid == kRootDirID
+                       && is_inappropriate_name(my_cat_desc.cd_nameptr, my_cat_desc.cd_namelen)) {
+                       myResult = 0;
+                       goto ExitThisRoutine;
+                   }
+               }
+
+               finder_info = (struct FndrDirInfo *)&my_cat_attr.ca_finderinfo[0];
+               if (   (searchBits & SRCHFS_SKIPINVISIBLE)
+                   && (SWAP_BE16(finder_info->frFlags) & kIsInvisible)) {
+                   
+                   myResult = 0;
+                   goto ExitThisRoutine;
+               }
+
                myNodeID = my_cat_desc.cd_parentcnid;   /* move up the hierarchy */
                myPerms = DerivePermissionSummary(my_cat_attr.ca_uid, my_cat_attr.ca_gid, 
                                                my_cat_attr.ca_mode, my_hfsmountPtr->hfs_mp,
                                                theProcPtr->p_ucred, theProcPtr  );
+
                cat_releasedesc( &my_cat_desc );
                
                if ( (myPerms & X_OK) == 0 )
@@ -551,7 +751,29 @@ CheckCriteria(     ExtendedVCB *vcb,
                break;
                        
        case kHFSFileRecord:
+               if ( (searchBits & SRCHFS_MATCHFILES) == 0 ) {  /* If we are NOT searching files */
+                       matched = false;
+                       goto TestDone;
+               }
+               break;
+
        case kHFSPlusFileRecord:
+               /* Check if hardlink links should be skipped. */
+               if (searchBits & SRCHFS_SKIPLINKS) {
+                       cnid_t parid = key->hfsPlus.parentID;
+                       HFSPlusCatalogFile *filep = (HFSPlusCatalogFile *)rec;
+
+                       if ((SWAP_BE32(filep->userInfo.fdType) == kHardLinkFileType) &&
+                           (SWAP_BE32(filep->userInfo.fdCreator) == kHFSPlusCreator)) {
+                               return (false); /* skip over link records */
+                       } else if ((parid == VCBTOHFS(vcb)->hfs_privdir_desc.cd_cnid) &&
+                                  (filep->bsdInfo.special.linkCount == 0)) {
+                               return (false); /* skip over unlinked files */
+                       }
+               } else if (key->hfsPlus.parentID == VCBTOHFS(vcb)->hfs_privdir_desc.cd_cnid) {
+                       return (false); /* skip over private files */
+               }
+
                if ( (searchBits & SRCHFS_MATCHFILES) == 0 ) {  /* If we are NOT searching files */
                        matched = false;
                        goto TestDone;
@@ -613,6 +835,42 @@ CheckCriteria(     ExtendedVCB *vcb,
        /* Convert catalog record into cat_attr format. */
        cat_convertattr(VCBTOHFS(vcb), rec, &c_attr, &datafork, &rsrcfork);
        
+       if (searchBits & SRCHFS_SKIPINVISIBLE) {
+           int flags;
+           
+           switch (rec->recordType) {
+               case kHFSFolderRecord:
+               case kHFSPlusFolderRecord: {
+                   struct FndrDirInfo *finder_info;
+                   
+                   finder_info = (struct FndrDirInfo *)&c_attr.ca_finderinfo[0];
+                   flags = SWAP_BE16(finder_info->frFlags);
+                   break;
+               }
+                       
+               case kHFSFileRecord:
+               case kHFSPlusFileRecord: {
+                   struct FndrFileInfo *finder_info;
+                   
+                   finder_info = (struct FndrFileInfo *)&c_attr.ca_finderinfo[0];
+                   flags = SWAP_BE16(finder_info->fdFlags);
+                   break;
+               }
+
+               default: {
+                   flags = kIsInvisible;
+                   break;
+               }
+           }
+                   
+           if (flags & kIsInvisible) {
+               matched = false;
+               goto TestDone;
+           }
+       }
+       
+                   
+
        /* Now that we have a record worth searching, see if it matches the search attributes */
        if (rec->recordType == kHFSFileRecord ||
            rec->recordType == kHFSPlusFileRecord) {
@@ -839,7 +1097,7 @@ InsertMatch( struct vnode *root_vp, struct uio *a_uio, CatalogRecord *rec,
        u_long packedBufferSize;
        ExtendedVCB *vcb = VTOVCB(root_vp);
        Boolean isHFSPlus = vcb->vcbSigWord == kHFSPlusSigWord;
-       u_long privateDir = VTOHFS(root_vp)->hfs_private_metadata_dir;
+       u_long privateDir = VTOHFS(root_vp)->hfs_privdir_desc.cd_cnid;
        struct attrblock attrblk;
        struct cat_desc c_desc = {0};
        struct cat_attr c_attr = {0};
@@ -858,6 +1116,14 @@ InsertMatch( struct vnode *root_vp, struct uio *a_uio, CatalogRecord *rec,
                goto exit;
        }
 
+       /* Hide the private journal files */
+       if (VTOHFS(root_vp)->jnl &&
+           ((c_attr.ca_fileid == VTOHFS(root_vp)->hfs_jnlfileid) ||
+            (c_attr.ca_fileid == VTOHFS(root_vp)->hfs_jnlinfoblkid))) {
+               err = 0;
+               goto exit;
+       }
+
        if (returnAttrList->commonattr & ATTR_CMN_NAME) {
                cat_convertkey(VTOHFS(root_vp), key, rec, &c_desc);
        } else {
@@ -868,19 +1134,13 @@ InsertMatch( struct vnode *root_vp, struct uio *a_uio, CatalogRecord *rec,
                        c_desc.cd_parentcnid = key->hfs.parentID;
        }
 
-       /* hide open files that have been deleted */
-       if ((privateDir != 0) && (c_desc.cd_parentcnid == privateDir)) {
-               err = 0;
-               goto exit;
-       }
-
        attrblk.ab_attrlist = returnAttrList;
        attrblk.ab_attrbufpp = &rovingAttributesBuffer;
        attrblk.ab_varbufpp = &rovingVariableBuffer;
        attrblk.ab_flags = 0;
        attrblk.ab_blocksize = 0;
 
-       hfs_packattrblk(&attrblk, VTOHFS(root_vp), NULL, &c_desc, &c_attr, &datafork, &rsrcfork);
+       hfs_packattrblk(&attrblk, VTOHFS(root_vp), NULL, &c_desc, &c_attr, &datafork, &rsrcfork, a_uio->uio_procp);
 
        packedBufferSize = (char*)rovingVariableBuffer - (char*)attributesBuffer;
 
@@ -983,12 +1243,8 @@ UnpackSearchAttributeBlock( struct vnode *vp, struct attrlist     *alist, searchinfo
                        ++((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);
+                       bcopy( attributeBuffer, searchInfo->finderInfo, sizeof(u_long) * 8 );
+                       (u_long *)attributeBuffer += 8;
                }
                if ( a & ATTR_CMN_OWNERID ) {
                        searchInfo->uid = *((uid_t *)attributeBuffer);