]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/vfs_lookup.c
xnu-3247.1.106.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_lookup.c
index 4beff12a6e9c4364e61e38158991e94e3b74d066..09bd470a99a30aa63493011100713210062a801a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2011 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2015 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -90,7 +90,7 @@
 #include <sys/kauth.h>
 #include <kern/kalloc.h>
 #include <security/audit/audit.h>
-
+#include <sys/dtrace.h>        /* to get the prototype for strstr() in sys/dtrace_glue.h */
 #if CONFIG_MACF
 #include <security/mac_framework.h>
 #endif
@@ -371,6 +371,7 @@ retry_copy:
                if ( (error = lookup(ndp)) ) {
                        goto error_out;
                }
+
                /*
                 * Check for symbolic link
                 */
@@ -633,15 +634,6 @@ lookup_handle_found_vnode(struct nameidata *ndp, struct componentname *cnp, int
                goto nextname;
        }
 
-#if CONFIG_TRIGGERS
-       if (dp->v_resolve) {
-               error = vnode_trigger_resolve(dp, ndp, ctx);
-               if (error) {
-                       goto out;
-               }
-       }
-#endif /* CONFIG_TRIGGERS */
-
        /*
         * Take into account any additional components consumed by
         * the underlying filesystem.
@@ -1315,73 +1307,90 @@ lookup_traverse_mountpoints(struct nameidata *ndp, struct componentname *cnp, vn
        uint32_t depth = 0;
        vnode_t mounted_on_dp;
        int current_mount_generation = 0;
+#if CONFIG_TRIGGERS
+       vnode_t triggered_dp = NULLVP;
+       int retry_cnt = 0;
+#define MAX_TRIGGER_RETRIES 1
+#endif
        
-       mounted_on_dp = dp;
-       current_mount_generation = mount_generation;
-
-       while ((dp->v_type == VDIR) && dp->v_mountedhere &&
-                       ((cnp->cn_flags & NOCROSSMOUNT) == 0)) {
+       if (dp->v_type != VDIR || cnp->cn_flags & NOCROSSMOUNT)
+               return 0;
 
-               if (dp->v_mountedhere->mnt_lflag & MNT_LFORCE) {
-                       break;  // don't traverse into a forced unmount
-               }
+       mounted_on_dp = dp;
 #if CONFIG_TRIGGERS
-               /*
-                * For a trigger vnode, call its resolver when crossing its mount (if requested)
-                */
-               if (dp->v_resolve) {
-                       (void) vnode_trigger_resolve(dp, ndp, ctx);
-               }
+restart:
 #endif
-               vnode_lock(dp);
-
-               if ((dp->v_type == VDIR) && (mp = dp->v_mountedhere)) {
+       current_mount_generation = mount_generation;
 
+       while (dp->v_mountedhere) {
+               vnode_lock_spin(dp);
+               if ((mp = dp->v_mountedhere)) {
                        mp->mnt_crossref++;
                        vnode_unlock(dp);
+               } else {
+                       vnode_unlock(dp);
+                       break;
+               }
 
+               if (ISSET(mp->mnt_lflag, MNT_LFORCE)) {
+                       mount_dropcrossref(mp, dp, 0);
+                       break;  // don't traverse into a forced unmount
+               }
 
-                       if (vfs_busy(mp, vbusyflags)) {
-                               mount_dropcrossref(mp, dp, 0);
-                               if (vbusyflags == LK_NOWAIT) {
-                                       error = ENOENT;
-                                       goto out;
-                               }
-
-                               continue;
-                       }
-
-                       error = VFS_ROOT(mp, &tdp, ctx);
 
+               if (vfs_busy(mp, vbusyflags)) {
                        mount_dropcrossref(mp, dp, 0);
-                       vfs_unbusy(mp);
-
-                       if (error) {
+                       if (vbusyflags == LK_NOWAIT) {
+                               error = ENOENT;
                                goto out;
                        }
 
-                       vnode_put(dp);
-                       ndp->ni_vp = dp = tdp;
-                       depth++;
+                       continue;
+               }
+
+               error = VFS_ROOT(mp, &tdp, ctx);
 
-#if CONFIG_TRIGGERS
-                       /*
-                        * Check if root dir is a trigger vnode
-                        */
-                       if (dp->v_resolve) {
-                               error = vnode_trigger_resolve(dp, ndp, ctx);
-                               if (error) {
-                                       goto out;
-                               }
-                       }
-#endif                 
+               mount_dropcrossref(mp, dp, 0);
+               vfs_unbusy(mp);
 
-               } else { 
-                       vnode_unlock(dp);
+               if (error) {
+                       goto out;
+               }
+
+               vnode_put(dp);
+               ndp->ni_vp = dp = tdp;
+               if (dp->v_type != VDIR) {
+#if DEVELOPMENT || DEBUG
+                       panic("%s : Root of filesystem not a directory\n",
+                           __FUNCTION__);
+#else
                        break;
+#endif
                }
+               depth++;
        }
 
+#if CONFIG_TRIGGERS
+       /*
+        * The triggered_dp check here is required but is susceptible to a
+        * (unlikely) race in which trigger mount is done from here and is
+        * unmounted before we get past vfs_busy above. We retry to deal with
+        * that case but it has the side effect of unwanted retries for
+        * "special" processes which don't want to trigger mounts.
+        */
+       if (dp->v_resolve && retry_cnt < MAX_TRIGGER_RETRIES) {
+               error = vnode_trigger_resolve(dp, ndp, ctx);
+               if (error)
+                       goto out;
+               if (dp == triggered_dp)
+                       retry_cnt += 1;
+               else
+                       retry_cnt = 0;
+               triggered_dp = dp;
+               goto restart;
+       }
+#endif /* CONFIG_TRIGGERS */
+
        if (depth) {
                mp = mounted_on_dp->v_mountedhere;