static int gfs_dir_lookup_dynamic(gfs_lookup_cb callback, gfs_dir_t *dp, const char *nm, struct vnode *dvp, struct vnode **vpp, cred_t *cr, int flags, int *direntflags, pathname_t *realpnp) { gfs_file_t *fp; ino64_t ino; int ret; ASSERT(GFS_DIR_LOCKED(dp)); /* * Drop the directory lock, as the lookup routine * will need to allocate memory, or otherwise deadlock on this * directory. */ gfs_dir_unlock(dp); ret = callback(dvp, nm, vpp, &ino, cr, flags, direntflags, realpnp); gfs_dir_lock(dp); /* * The callback for extended attributes returns a vnode * with v_data from an underlying fs. */ if (ret == 0 /*&& !IS_XATTRDIR(dvp)*/) { fp = (gfs_file_t *)((vnode_fsnode(*vpp))); fp->gfs_index = -1; fp->gfs_ino = ino; } return (ret); }
/* * gfs_dir_lookup() * * Looks up the given name in the directory and returns the corresponding * vnode, if found. * * First, we search statically defined entries, if any, with a call to * gfs_dir_lookup_static(). If no static entry is found, and we have * a callback function we try a dynamic lookup via gfs_dir_lookup_dynamic(). * * This function returns 0 on success, non-zero on error. */ int gfs_dir_lookup(struct vnode *dvp, const char *nm, struct vnode **vpp, cred_t *cr, int flags, int *direntflags, pathname_t *realpnp) { gfs_dir_t *dp = vnode_fsnode(dvp); boolean_t casecheck; struct vnode *dynvp = NULL; struct vnode *vp = NULL; int (*compare)(const char *, const char *); int error, idx; dprintf("gfs_dir_lookup\n"); ASSERT(dvp->v_type == VDIR); if (gfs_lookup_dot(vpp, dvp, dp->gfsd_file.gfs_parent, nm) == 0) return (0); casecheck = (flags & FIGNORECASE) != 0 && direntflags != NULL; #if 1 //FIXME if (/*vfs_has_feature(vnode_mount(dvp), VFSFT_NOCASESENSITIVE) ||*/ (flags & FIGNORECASE)) compare = strcasecmp; else #endif compare = strcmp; gfs_dir_lock(dp); error = gfs_dir_lookup_static(compare, dp, nm, dvp, &idx, &vp, realpnp); if (vp && casecheck) { gfs_dirent_t *ge; int i; for (i = idx + 1; i < dp->gfsd_nstatic; i++) { ge = &dp->gfsd_static[i]; if (strcasecmp(ge->gfse_name, nm) == 0) { *direntflags |= ED_CASE_CONFLICT; goto out; } } } #if 0 if ((error || casecheck) && dp->gfsd_lookup) error = gfs_dir_lookup_dynamic(dp->gfsd_lookup, dp, nm, dvp, &dynvp, cr, flags, direntflags, vp ? NULL : realpnp); #endif if (vp && dynvp) { /* static and dynamic entries are case-insensitive conflict */ ASSERT(casecheck); *direntflags |= ED_CASE_CONFLICT; VN_RELE(dynvp); } else if (vp == NULL) { vp = dynvp; } else if (error == ENOENT) { error = 0; } else if (error) { VN_RELE(vp); vp = NULL; } out: gfs_dir_unlock(dp); *vpp = vp; return (error); }
/* * gfs_dir_lookup_static() * * This routine looks up the provided name amongst the static entries * in the gfs directory and returns the corresponding vnode, if found. * The first argument to the function is a pointer to the comparison * function this function should use to decide if names are a match. * * If a match is found, and GFS_CACHE_VNODE is set and the vnode * exists, we simply return the existing vnode. Otherwise, we call * the static entry's callback routine, caching the result if * necessary. If the idx pointer argument is non-NULL, we use it to * return the index of the matching static entry. * * The gfs directory is expected to be locked by the caller prior to calling * this function. The directory may be unlocked during the execution of * this function, but will be locked upon return from the function. * * This function returns 0 if a match is found, ENOENT if not. */ static int gfs_dir_lookup_static(int (*compare)(const char *, const char *), gfs_dir_t *dp, const char *nm, struct vnode *dvp, int *idx, struct vnode **vpp, pathname_t *rpnp) { gfs_dirent_t *ge; struct vnode *vp = NULL; int i; ASSERT(GFS_DIR_LOCKED(dp)); /* * Search static entries. */ for (i = 0; i < dp->gfsd_nstatic; i++) { ge = &dp->gfsd_static[i]; if (compare(ge->gfse_name, nm) == 0) { if (rpnp) (void) strlcpy(rpnp->pn_buf, ge->gfse_name, rpnp->pn_bufsize); if (ge->gfse_vnode) { ASSERT(ge->gfse_flags & GFS_CACHE_VNODE); vp = ge->gfse_vnode; VN_HOLD(vp); break; } /* * We drop the directory lock, as the constructor will * need to do KM_SLEEP allocations. If we return from * the constructor only to find that a parallel * operation has completed, and GFS_CACHE_VNODE is set * for this entry, we discard the result in favor of * the cached vnode. */ dprintf("lookup_static\n"); gfs_dir_unlock(dp); vp = ge->gfse_ctor(dvp); gfs_dir_lock(dp); ((gfs_file_t *)vnode_fsnode(vp))->gfs_index = i; /* Set the inode according to the callback. */ ((gfs_file_t *)vnode_fsnode(vp))->gfs_ino = dp->gfsd_inode(dvp, i); if (ge->gfse_flags & GFS_CACHE_VNODE) { if (ge->gfse_vnode == NULL) { ge->gfse_vnode = vp; } else { /* * A parallel constructor beat us to it; * return existing vnode. We have to be * careful because we can't release the * current vnode while holding the * directory lock; its inactive routine * will try to lock this directory. */ struct vnode *oldvp = vp; vp = ge->gfse_vnode; VN_HOLD(vp); gfs_dir_unlock(dp); VN_RELE(oldvp); gfs_dir_lock(dp); } } break; } } if (vp == NULL) return (ENOENT); else if (idx) *idx = i; *vpp = vp; return (0); }
/* * gfs_file_inactive() * * Called from the VOP_INACTIVE() routine. If necessary, this routine will * remove the given vnode from the parent directory and clean up any references * in the VFS layer. * * If the vnode was not removed (due to a race with vget), then NULL is * returned. Otherwise, a pointer to the private data is returned. */ void * gfs_file_inactive(struct vnode *vp) { int i; gfs_dirent_t *ge = NULL; gfs_file_t *fp = vnode_fsnode(vp); gfs_dir_t *dp = NULL; void *data; if (!fp) return NULL; if (fp->gfs_parent == NULL /*|| (vp->v_flag & V_XATTRDIR)*/) goto found; /* * XXX cope with a FreeBSD-specific race wherein the parent's * snapshot data can be freed before the parent is */ if ((dp = vnode_fsnode(fp->gfs_parent)) == NULL) return (NULL); /* * First, see if this vnode is cached in the parent. */ gfs_dir_lock(dp); /* * Find it in the set of static entries. */ for (i = 0; i < dp->gfsd_nstatic; i++) { ge = &dp->gfsd_static[i]; if (ge->gfse_vnode == vp) goto found; } /* * If 'ge' is NULL, then it is a dynamic entry. */ ge = NULL; found: #ifdef TODO if (vp->v_flag & V_XATTRDIR) VI_LOCK(fp->gfs_parent); #endif VN_HOLD(vp); /* * Really remove this vnode */ data = vnode_fsnode(vp); if (ge != NULL) { /* * If this was a statically cached entry, simply set the * cached vnode to NULL. */ ge->gfse_vnode = NULL; } VN_RELE(vp); /* * Free vnode and release parent */ dprintf("freeing vp %p and parent %p\n", vp, fp->gfs_parent); if (fp->gfs_parent) { if (dp) gfs_dir_unlock(dp); //VOP_UNLOCK(vp, 0); VN_RELE(fp->gfs_parent); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); } else { ASSERT(vp->v_vfsp != NULL); VFS_RELE(vp->v_vfsp); } #ifdef TODO if (vp->v_flag & V_XATTRDIR) VI_UNLOCK(fp->gfs_parent); #endif return (data); }
/* * gfs_file_inactive() * * Called from the VOP_INACTIVE() routine. If necessary, this routine will * remove the given vnode from the parent directory and clean up any references * in the VFS layer. * * If the vnode was not removed (due to a race with vget), then NULL is * returned. Otherwise, a pointer to the private data is returned. */ void * gfs_file_inactive(vnode_t *vp) { int i; gfs_dirent_t *ge = NULL; gfs_file_t *fp = vp->v_data; gfs_dir_t *dp = NULL; void *data; if (fp->gfs_parent == NULL || (vp->v_flag & V_XATTRDIR)) goto found; dp = fp->gfs_parent->v_data; /* * First, see if this vnode is cached in the parent. */ gfs_dir_lock(dp); /* * Find it in the set of static entries. */ for (i = 0; i < dp->gfsd_nstatic; i++) { ge = &dp->gfsd_static[i]; if (ge->gfse_vnode == vp) goto found; } /* * If 'ge' is NULL, then it is a dynamic entry. */ ge = NULL; found: if (vp->v_flag & V_XATTRDIR) { mutex_enter(&fp->gfs_parent->v_lock); } mutex_enter(&vp->v_lock); if (vp->v_count == 1) { /* * Really remove this vnode */ data = vp->v_data; if (ge != NULL) { /* * If this was a statically cached entry, simply set the * cached vnode to NULL. */ ge->gfse_vnode = NULL; } if (vp->v_flag & V_XATTRDIR) { fp->gfs_parent->v_xattrdir = NULL; mutex_exit(&fp->gfs_parent->v_lock); } mutex_exit(&vp->v_lock); /* * Free vnode and release parent */ if (fp->gfs_parent) { if (dp) { gfs_dir_unlock(dp); } VN_RELE(fp->gfs_parent); } else { ASSERT(vp->v_vfsp != NULL); VFS_RELE(vp->v_vfsp); } vn_free(vp); } else { VN_RELE_LOCKED(vp); data = NULL; mutex_exit(&vp->v_lock); if (vp->v_flag & V_XATTRDIR) { mutex_exit(&fp->gfs_parent->v_lock); } if (dp) gfs_dir_unlock(dp); } return (data); }
/* * gfs_file_inactive() * * Called from the VOP_INACTIVE() routine. If necessary, this routine will * remove the given vnode from the parent directory and clean up any references * in the VFS layer. * * If the vnode was not removed (due to a race with vget), then NULL is * returned. Otherwise, a pointer to the private data is returned. */ void * gfs_file_inactive(vnode_t *vp) { int i; gfs_dirent_t *ge = NULL; gfs_file_t *fp = vp->v_data; gfs_dir_t *dp = NULL; void *data; if (fp->gfs_parent == NULL || (vp->v_flag & V_XATTRDIR)) goto found; /* * XXX cope with a FreeBSD-specific race wherein the parent's * snapshot data can be freed before the parent is */ if ((dp = fp->gfs_parent->v_data) == NULL) return (NULL); /* * First, see if this vnode is cached in the parent. */ gfs_dir_lock(dp); /* * Find it in the set of static entries. */ for (i = 0; i < dp->gfsd_nstatic; i++) { ge = &dp->gfsd_static[i]; if (ge->gfse_vnode == vp) goto found; } /* * If 'ge' is NULL, then it is a dynamic entry. */ ge = NULL; found: if (vp->v_flag & V_XATTRDIR) VI_LOCK(fp->gfs_parent); VI_LOCK(vp); /* * Really remove this vnode */ data = vp->v_data; if (ge != NULL) { /* * If this was a statically cached entry, simply set the * cached vnode to NULL. */ ge->gfse_vnode = NULL; } VI_UNLOCK(vp); /* * Free vnode and release parent */ if (fp->gfs_parent) { if (dp) gfs_dir_unlock(dp); VI_LOCK(fp->gfs_parent); fp->gfs_parent->v_usecount--; VI_UNLOCK(fp->gfs_parent); } else { ASSERT(vp->v_vfsp != NULL); VFS_RELE(vp->v_vfsp); } if (vp->v_flag & V_XATTRDIR) VI_UNLOCK(fp->gfs_parent); return (data); }