]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/hfs/hfs_lookup.c
xnu-3248.60.10.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_lookup.c
index 2200fe1de12b88b26ab469a68421b515a55495e1..c46bce7c7d38505b4e0c7de94f6c8e3a56b1b828 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999-2012 Apple Inc. All rights reserved.
+ * Copyright (c) 1999-2015 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -76,6 +76,7 @@
 #include <sys/file.h>
 #include <sys/mount.h>
 #include <sys/vnode.h>
+#include <sys/vnode_internal.h>
 #include <sys/malloc.h>
 #include <sys/kdebug.h>
 #include <sys/kauth.h>
  *     When should we lock parent_hp in here ??
  */
 static int
-hfs_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, int *cnode_locked)
+hfs_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, int *cnode_locked, int force_casesensitive_lookup)
 {
        struct cnode *dcp;      /* cnode for directory being searched */
        struct vnode *tvp;      /* target vnode */
@@ -190,7 +191,7 @@ hfs_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, int
                cnp->cn_flags &= ~MAKEENTRY;
                goto found;     /* We always know who we are */
        } else {
-               if (hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK) != 0) {
+               if (hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
                        retval = ENOENT;  /* The parent no longer exists ? */
                        goto exit;
                }
@@ -207,10 +208,15 @@ hfs_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, int
                    goto retry;
                }
 
-               /* No need to go to catalog if there are no children */
-               if (dcp->c_entries == 0) {
-                       goto notfound;
-               }
+
+               /*
+                * We shouldn't need to go to the catalog if there are no children.
+                * However, in the face of a minor disk corruption where the valence of
+                * the directory is off, we could infinite loop here if we return ENOENT
+                * even though there are actually items in the directory.  (create will
+                * see the ENOENT, try to create something, which will return with 
+                * EEXIST over and over again).  As a result, always check the catalog.
+                */
 
                bzero(&cndesc, sizeof(cndesc));
                cndesc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
@@ -220,7 +226,7 @@ hfs_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, int
 
                lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
 
-               retval = cat_lookup(hfsmp, &cndesc, 0, &desc, &attr, &fork, NULL);
+               retval = cat_lookup(hfsmp, &cndesc, 0, force_casesensitive_lookup, &desc, &attr, &fork, NULL);
                
                hfs_systemfile_unlock(hfsmp, lockflags);
 
@@ -242,7 +248,7 @@ hfs_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, int
                        
                        goto found;
                }
-notfound:
+               
                /*
                 * ENAMETOOLONG supersedes other errors
                 *
@@ -255,6 +261,25 @@ notfound:
                        retval = ENAMETOOLONG;
                } else if (retval == 0) {
                        retval = ENOENT;
+               } else if (retval == ERESERVEDNAME) {
+                       /*
+                        * We found the name in the catalog, but it is unavailable
+                        * to us. The exact error to return to our caller depends
+                        * on the operation, and whether we've already reached the
+                        * last path component. In all cases, avoid a negative
+                        * cache entry, since someone else may be able to access
+                        * the name if their lookup is configured differently.
+                        */
+
+                       cnp->cn_flags &= ~MAKEENTRY;
+
+                       if (((flags & ISLASTCN) == 0) || ((nameiop == LOOKUP) || (nameiop == DELETE))) {
+                               /* A reserved name for a pure lookup is the same as the path not being present */
+                               retval = ENOENT;
+                       } else {
+                               /* A reserved name with intent to create must be rejected as impossible */
+                               retval = EEXIST;
+                       }
                }
                if (retval != ENOENT)
                        goto exit;
@@ -265,10 +290,7 @@ notfound:
                 * directory has not been removed, then can consider
                 * allowing file to be created.
                 */
-               if ((nameiop == CREATE || nameiop == RENAME ||
-                   (nameiop == DELETE &&
-                   (cnp->cn_flags & DOWHITEOUT) &&
-                   (cnp->cn_flags & ISWHITEOUT))) &&
+               if ((nameiop == CREATE || nameiop == RENAME) &&
                    (flags & ISLASTCN) &&
                    !(ISSET(dcp->c_flag, C_DELETED | C_NOEXISTS))) {
                        retval = EJUSTRETURN;
@@ -312,7 +334,8 @@ found:
                 * Directory hard links can have multiple parents so
                 * find the appropriate parent for the current thread.
                 */
-               if ((retval = hfs_vget(hfsmp, hfs_currentparent(VTOC(dvp)), &tvp, 0, 0))) {
+               if ((retval = hfs_vget(hfsmp, hfs_currentparent(VTOC(dvp),
+                                                                       /* have_lock: */ false), &tvp, 0, 0))) {
                        goto exit;
                }
                *cnode_locked = 1;
@@ -398,12 +421,9 @@ found:
                 * Save the origin info for file and directory hardlinks.  Directory hardlinks 
                 * need the origin for '..' lookups, and file hardlinks need it to ensure that 
                 * competing lookups do not cause us to vend different hardlinks than the ones requested.
-                * We want to restrict saving the cache entries to LOOKUP namei operations, since
-                * we're really doing this to protect getattr.
                 */
-               if ((nameiop == LOOKUP) && (VTOC(tvp)->c_flag & C_HARDLINK)) {
+               if (ISSET(VTOC(tvp)->c_flag, C_HARDLINK))
                        hfs_savelinkorigin(VTOC(tvp), VTOC(dvp)->c_fileid);
-               }
                *cnode_locked = 1;
                *vpp = tvp;
        }
@@ -448,14 +468,23 @@ hfs_vnop_lookup(struct vnop_lookup_args *ap)
        int error;
        struct vnode **vpp = ap->a_vpp;
        struct componentname *cnp = ap->a_cnp;
+       struct proc *p = vfs_context_proc(ap->a_context);
        int flags = cnp->cn_flags;
+       int force_casesensitive_lookup = proc_is_forcing_hfs_case_sensitivity(p);
        int cnode_locked;
+       int fastdev_candidate = 0;
+       int auto_candidate = 0;
 
        *vpp = NULL;
        dcp = VTOC(dvp);
-       
        hfsmp = VTOHFS(dvp);
 
+       if ((hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && (vnode_isfastdevicecandidate(dvp) || (dcp->c_attr.ca_recflags & kHFSFastDevCandidateMask)) ){
+               fastdev_candidate = 1;
+               auto_candidate = (vnode_isautocandidate(dvp) || (dcp->c_attr.ca_recflags & kHFSAutoCandidateMask));
+       }
+       
+
        /*
         * Lookup an entry in the cache
         *
@@ -490,7 +519,10 @@ hfs_vnop_lookup(struct vnop_lookup_args *ap)
                goto exit;
        }
        
-       
+       if (cp->c_attr.ca_recflags & kHFSDoNotFastDevPinMask) {
+               fastdev_candidate = 0;
+       }
+
        /*
         * If this is a hard-link vnode then we need to update
         * the name (of the link), the parent ID, the cnid, the
@@ -498,15 +530,36 @@ hfs_vnop_lookup(struct vnop_lookup_args *ap)
         * getattrlist calls to return the correct link info.
         */
 
-       if ((flags & ISLASTCN) && (cp->c_flag & C_HARDLINK)) {
+       /*
+        * Alternatively, if we are forcing a case-sensitive lookup
+        * on a case-insensitive volume, the namecache entry
+        * may have been for an incorrect case. Since we cannot
+        * determine case vs. normalization, redrive the catalog
+        * lookup based on any byte mismatch.
+        */
+       if (((flags & ISLASTCN) && (cp->c_flag & C_HARDLINK))
+               || (force_casesensitive_lookup && !(hfsmp->hfs_flags & HFS_CASE_SENSITIVE))) {
                int stale_link = 0;
 
-               hfs_lock(cp, HFS_FORCE_LOCK);   
+               hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);      
                if ((cp->c_parentcnid != dcp->c_cnid) ||
+                   (cnp->cn_namelen != cp->c_desc.cd_namelen) ||
                    (bcmp(cnp->cn_nameptr, cp->c_desc.cd_nameptr, cp->c_desc.cd_namelen) != 0)) {
                        struct cat_desc desc;
                        struct cat_attr lookup_attr;
                        int lockflags;
+
+                       if (force_casesensitive_lookup && !(hfsmp->hfs_flags & HFS_CASE_SENSITIVE)) {
+                               /*
+                                * Since the name in the cnode doesn't match our lookup
+                                * string exactly, do a full lookup.
+                                */
+                               hfs_unlock (cp);
+
+                               vnode_put(vp);
+                               goto lookup;
+                       }
+
                        /*
                         * Get an updated descriptor
                         */
@@ -541,7 +594,7 @@ hfs_vnop_lookup(struct vnop_lookup_args *ap)
 
                        lockflags = hfs_systemfile_lock(VTOHFS(dvp), SFL_CATALOG, HFS_SHARED_LOCK);             
                
-                       error = cat_lookup(VTOHFS(vp), &desc, 0, &desc, &lookup_attr, NULL, NULL);      
+                       error = cat_lookup(VTOHFS(vp), &desc, 0, 0, &desc, &lookup_attr, NULL, NULL);   
                        
                        hfs_systemfile_unlock(VTOHFS(dvp), lockflags);
 
@@ -559,12 +612,8 @@ hfs_vnop_lookup(struct vnop_lookup_args *ap)
                                         * Save the origin info for file and directory hardlinks.  Directory hardlinks 
                                         * need the origin for '..' lookups, and file hardlinks need it to ensure that 
                                         * competing lookups do not cause us to vend different hardlinks than the ones requested.
-                                        * We want to restrict saving the cache entries to LOOKUP namei operations, since
-                                        * we're really doing this to protect getattr.
                                         */
-                                       if (cnp->cn_nameiop == LOOKUP) {
-                                               hfs_savelinkorigin(cp, dcp->c_fileid);
-                                       }
+                                       hfs_savelinkorigin(cp, dcp->c_fileid);
                                }
                                else {
                                        /* If the fileID does not match then do NOT replace the descriptor! */
@@ -604,11 +653,27 @@ lookup:
         */
        cnode_locked = 0;
 
-       error = hfs_lookup(dvp, vpp, cnp, &cnode_locked);
+       error = hfs_lookup(dvp, vpp, cnp, &cnode_locked, force_casesensitive_lookup);
+       
+       if (*vpp && (VTOC(*vpp)->c_attr.ca_recflags & kHFSDoNotFastDevPinMask)) {
+               fastdev_candidate = 0;
+       }
+
+       if (*vpp && (VTOC(*vpp)->c_attr.ca_recflags & kHFSAutoCandidateMask)) {
+               //printf("vp %s / %d is an auto-candidate\n", (*vpp)->v_name ? (*vpp)->v_name : "no-name", VTOC(*vpp)->c_fileid);
+               auto_candidate = 1;
+       }
        
        if (cnode_locked)
                hfs_unlock(VTOC(*vpp));
 exit:
+       if (*vpp && fastdev_candidate && (*vpp)->v_parent == dvp && !(vnode_isfastdevicecandidate(*vpp))) {
+               vnode_setfastdevicecandidate(*vpp);
+               if (auto_candidate) {
+                       vnode_setautocandidate(*vpp);
+               }
+       }
+
        {
        uthread_t ut = (struct uthread *)get_bsdthread_info(current_thread());
 
@@ -623,7 +688,7 @@ exit:
         * completed and throttling at the systemcall return
         */
        if (__improbable(ut->uu_lowpri_window)) {
-               throttle_lowpri_io(TRUE);
+               throttle_lowpri_io(1);
        }
        }