Beispiel #1
0
Datei: gfs.c Projekt: RJVB/zfs
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);
}
Beispiel #2
0
Datei: gfs.c Projekt: RJVB/zfs
/*
 * 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);
}
Beispiel #3
0
Datei: gfs.c Projekt: RJVB/zfs
/*
 * 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);
}
Beispiel #4
0
Datei: gfs.c Projekt: RJVB/zfs
/*
 * 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);
}
Beispiel #5
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(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);
}
Beispiel #6
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(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);
}