X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/c18c124eaa464aaaa5549e99e5a70fc9cbb50944..3e170ce000f1506b7b5d2c5c7faec85ceabb573d:/bsd/vfs/vfs_lookup.c?ds=inline diff --git a/bsd/vfs/vfs_lookup.c b/bsd/vfs/vfs_lookup.c index 4beff12a6..09bd470a9 100644 --- a/bsd/vfs/vfs_lookup.c +++ b/bsd/vfs/vfs_lookup.c @@ -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 #include #include - +#include /* to get the prototype for strstr() in sys/dtrace_glue.h */ #if CONFIG_MACF #include #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;