/* * 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); }
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); }
/* * 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; }
static int nm_fid(vnode_t *vp, struct fid *fidnodep, caller_context_t *ct) { return (VOP_FID(VTONM(vp)->nm_filevp, fidnodep, ct)); }