/*
- * 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@
*
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,
};
*/
+__private_extern__
int
hfs_search( ap )
struct vop_searchfs_args *ap; /*
CatalogRecord * myCurrentDataPtr;
CatPosition * myCatPositionPtr;
BTScanState myBTScanState;
+ void *user_start = NULL;
+ int user_len;
+ int32_t searchTime;
/* XXX Parameter check a_searchattrs? */
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,
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)
/* 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);
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,
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,
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;
}
}
ExitThisRoutine:
FREE( attributesBuffer, M_TEMP );
+ if (VTOHFS(ap->a_vp)->jnl && user_start) {
+ vsunlock(user_start, user_len, TRUE);
+ }
+
return (MacToVFSError(err));
}
&& (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;
}
}
}
+
+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;
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;
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 )
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;
/* 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) {
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};
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 {
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;
++((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);