Example #1
0
/*
 * A version of VOP_FID that deals with a remote VOP_FID for nfs.
 * If vp is an nfs node, nfs4_fid() returns EREMOTE, nfs3_fid() and nfs_fid()
 * returns the filehandle of vp as its fid. When nfs uses fid to set the
 * exportinfo filehandle template, a remote nfs filehandle would be too big for
 * the fid of the exported directory. This routine remaps the value of the
 * attribute va_nodeid of vp to be the fid of vp, so that the fid can fit.
 *
 * We need this fid mainly for setting up NFSv4 server namespace where an
 * nfs filesystem is also part of it. Thus, need to be able to setup a pseudo
 * exportinfo for an nfs node.
 *
 * e.g. mount a filesystem on top of a nfs dir, and then share the new mount
 *      (like exporting a local disk from a "diskless" client)
 */
int
vop_fid_pseudo(vnode_t *vp, fid_t *fidp)
{
	struct vattr va;
	int error;

	error = VOP_FID(vp, fidp, NULL);

	/*
	 * XXX nfs4_fid() does nothing and returns EREMOTE.
	 * XXX nfs3_fid()/nfs_fid() returns nfs filehandle as its fid
	 * which has a bigger length than local fid.
	 * NFS_FH4MAXDATA is the size of
	 * fhandle4_t.fh_xdata[NFS_FH4MAXDATA].
	 *
	 * Note: nfs[2,3,4]_fid() only gets called for diskless clients.
	 */
	if (error == EREMOTE ||
	    (error == 0 && fidp->fid_len > NFS_FH4MAXDATA)) {

		va.va_mask = AT_NODEID;
		error = VOP_GETATTR(vp, &va, 0, CRED(), NULL);
		if (error)
			return (error);

		fidp->fid_len = sizeof (va.va_nodeid);
		bcopy(&va.va_nodeid, fidp->fid_data, fidp->fid_len);
		return (0);
	}

	return (error);
}
/*ARGSUSED*/
static int
zfsctl_shares_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
{
	zfsvfs_t	*zfsvfs = vp->v_vfsp->vfs_data;
	znode_t		*dzp;
	int		error;

	ZFS_ENTER(zfsvfs);

	if (zfsvfs->z_shares_dir == 0) {
		ZFS_EXIT(zfsvfs);
		return (ENOTSUP);
	}

	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp)) == 0) {
		error = VOP_FID(ZTOV(dzp), fidp, ct);
		VN_RELE(ZTOV(dzp));
	}

	ZFS_EXIT(zfsvfs);
	return (error);
}
Example #3
0
static int
xattr_common_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
{
	xattr_fid_t	*xfidp;
	vnode_t		*pvp, *savevp;
	int		error;
	uint16_t	orig_len;

	if (fidp->fid_len < XATTR_FIDSZ) {
		fidp->fid_len = XATTR_FIDSZ;
		return (ENOSPC);
	}

	savevp = pvp = gfs_file_parent(vp);
	mutex_enter(&savevp->v_lock);
	if (pvp->v_flag & V_XATTRDIR) {
		pvp = gfs_file_parent(pvp);
	}
	mutex_exit(&savevp->v_lock);

	xfidp = (xattr_fid_t *)fidp;
	orig_len = fidp->fid_len;
	fidp->fid_len = sizeof (xfidp->parent_fid);

	error = VOP_FID(pvp, fidp, ct);
	if (error) {
		fidp->fid_len = orig_len;
		return (error);
	}

	xfidp->parent_len = fidp->fid_len;
	fidp->fid_len = XATTR_FIDSZ;
	xfidp->dir_offset = gfs_file_inode(vp);

	return (0);
}
Example #4
0
/*
 * NFS access to vnode file systems.
 *
 * We provide dentry/inode_to_fh() and fh_to_dentry() methods so that the
 * vnode-based file system can hook up its VOP_FID() and VFS_VGET()
 * methods.  The Linux NFS server calls these methods when encoding an
 * object into a file handle to be passed to the client for future
 * use, and when decoding a file handle and looking for the file
 * system object it describes.
 *
 * VOP_FID() takes a vnode and provides a file ID (fid) that can later
 * be presented (in a pair with a VFS pointer) to VFS_VGET() to
 * reconstitute that vnode.  In a Sun ONC-NFS style kernel, VOP_FID()
 * is used twice per file handle, once for the exported directory and
 * once for the object itself.  In Linux, the NFS layer itself handles
 * the export tree checking (depending on the status of
 * NFSEXP_NOSUBTREECHECK), so the file system only needs to fill in
 * the file handle with details for the object itself.  We always
 * provide both object and parent in the file handle to be sure that
 * we don't end up short on file handle space in a future call that
 * requires both.
 *
 * On a call from the NFS client, the Linux NFS layer finds a
 * superblock pointer from the file handle passed by the NFS client,
 * then calls the fh_to_dentry() method to get a dentry.  Sun ONC-NFS
 * kernels call VFS_VGET() on a vfsp, passing the FID portion of the
 * file handle.  In this layer, we unpack the file handle, determine
 * whether the parent or the object is needed, and pass the info along
 * to a VFS_VGET() call.  Once that returns, we look for an attached
 * dentry and use it, or fabricate a new one which NFS will attempt to
 * reconnect to the namespace.
 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
int
vnlayer_inode_to_fh(
    struct inode *inode,
    __u32 *fh,
    int *lenp,
    struct inode *parent
)
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) */
int
vnlayer_dentry_to_fh(
    struct dentry *dent,
    __u32 *fh,
    int *lenp,
    int need_parent
)
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) */
{
    int error;
    int type;
    int mylen;
    MDKI_FID_T *lfidp = NULL;
    MDKI_FID_T *parent_fidp = NULL;
    mdki_boolean_t bailout_needed = TRUE; /* Assume we'll fail. */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
    SUPER_T *sbp;
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)
    struct inode *inode = dent->d_inode;
    struct inode *parent = dent->d_parent->d_inode;
#endif

    /*
     * We use the type byte (return value) to encode the FH length.  Since we
     * always include two FIDs of the same size, the type must be even, so
     * that's how we "encode" the length of each FID (i.e. it is half the total
     * length).
     *
     * Always include parent entry; this makes sure that we only work with NFS
     * protocols that have enough room for our file handles.  (Without this, we
     * may return a directory file handle OK yet be unable to return a plain
     * file handle.)  Currently, we can just barely squeeze two standard
     * 10-byte vnode FIDs into the NFS v2 file handle.  The NFS v3 handle has
     * plenty of room.
     */
    ASSERT(ITOV(inode));
    error = VOP_FID(ITOV(inode), &lfidp);
    if (error != 0) {
        ASSERT(lfidp == NULL);
        goto bailout;
    }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
    /* we may be called with a NULL parent */
    if (parent == NULL) {
        /* in this case, fabricate a fake parent */
        parent_fidp = (MDKI_FID_T *) KMEM_ALLOC(MDKI_FID_LEN(lfidp),
                                                KM_SLEEP);
        if (parent_fidp == NULL) {
            MDKI_VFS_LOG(VFS_LOG_ERR, "%s: can't allocate %d bytes\n",
                         __func__,
                         (int) MDKI_FID_LEN(lfidp));
            goto bailout;
        }
        memset(parent_fidp, 0xff, MDKI_FID_LEN(lfidp));
        parent_fidp->fid_len = lfidp->fid_len;
    } else
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) */
    {
        error = VOP_FID(ITOV(parent), &parent_fidp);
        if (error != 0) {
            ASSERT(parent_fidp == NULL);
            goto bailout;
        }
    }

    /*
     * Our encoding scheme can't tolerate different length FIDs
     * (because otherwise the type wouldn't be guaranteed to be even).
     */
    if (parent_fidp->fid_len != lfidp->fid_len) {
        MDKI_VFS_LOG(VFS_LOG_ERR,
                     "%s: unbalanced parent/child fid lengths: %d, %d\n",
                     __func__, parent_fidp->fid_len, lfidp->fid_len);
        goto bailout;
    }

    /* 
     * vnode layer needs to release the storage for a fid on
     * Linux.  The VOP_FID() function allocates its own fid in
     * non-error cases.  Other UNIX systems release this storage
     * in the caller of VOP_FID, so we have to do it here.  We
     * copy the vnode-style fid into the caller-allocated space,
     * then free our allocated version here.
     *
     * Remember: vnode lengths are counting bytes, Linux lengths count __u32
     * units.
     */
    type = parent_fidp->fid_len + lfidp->fid_len; /* Guaranteed even. */
    mylen = roundup(type + MDKI_FID_EXTRA_SIZE, sizeof(*fh));

    if (mylen == VNODE_NFS_FH_TYPE_RESERVED ||
        mylen >= VNODE_NFS_FH_TYPE_ERROR)
    {
        MDKI_VFS_LOG(VFS_LOG_ESTALE,
                     "%s: required length %d out of range (%d,%d)\n",
                     __func__, mylen,
                     VNODE_NFS_FH_TYPE_RESERVED, VNODE_NFS_FH_TYPE_ERROR);
        goto bailout;
    }
    if (((*lenp) * sizeof(*fh)) < mylen) {
        MDKI_VFS_LOG(VFS_LOG_ESTALE,
                     "%s: need %d bytes for FH, have %d\n",
                     __func__, mylen, (int) (sizeof(*fh) * (*lenp)));
        goto bailout;
    }
    /* Copy FIDs into file handle. */
    *lenp = mylen / sizeof(*fh); /* No remainder because of roundup above. */
    BZERO(fh, mylen);           /* Zero whole fh to round up to __u32 boundary */
    BCOPY(lfidp->fid_data, fh, lfidp->fid_len);
    BCOPY(parent_fidp->fid_data, ((caddr_t)fh) + (type / 2),
          parent_fidp->fid_len);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
    /* 
     * For 64 bits OS, use a 32 bits hash of the SB pointer.
     * For 32 bits OS, use the pointer itself.
     */
    if (ITOV(inode) == NULL || 
        ITOV(inode)->v_vfsmnt == NULL) {
        MDKI_VFS_LOG(VFS_LOG_ESTALE,
                     "%s: %p is this a MVFS inode?\n",
                     __func__, inode);
        goto bailout;
    } else {
        sbp = ((struct vfsmount *)ITOV(inode)->v_vfsmnt)->mnt_sb;
    }
    MDKI_FID_SET_SB_HASH(fh, type / 2, MDKI_FID_CALC_HASH(sbp));
#endif

    bailout_needed = FALSE;         /* We're home free now. */

    if (bailout_needed) {
  bailout:
        type = VNODE_NFS_FH_TYPE_ERROR;
        *lenp = 0;
    }
#ifdef KMEMDEBUG
    if (lfidp != NULL)
        REAL_KMEM_FREE(lfidp, MDKI_FID_LEN(lfidp));
    if (parent_fidp != NULL)
        REAL_KMEM_FREE(parent_fidp, MDKI_FID_LEN(parent_fidp));
#else
    if (lfidp != NULL)
        KMEM_FREE(lfidp, MDKI_FID_LEN(lfidp));
    if (parent_fidp != NULL)
        KMEM_FREE(parent_fidp, MDKI_FID_LEN(parent_fidp));
#endif
    return type;
}
Example #5
0
static int
nm_fid(vnode_t *vp, struct fid *fidnodep, caller_context_t *ct)
{
	return (VOP_FID(VTONM(vp)->nm_filevp, fidnodep, ct));
}