/* * Copyright (c) 2004-2005 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "xfs.h" #include "xfs_types.h" #include "xfs_dmapi.h" #include "xfs_log.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_dir.h" #include "xfs_mount.h" #include "xfs_export.h" /* * XFS encodes and decodes the fileid portion of NFS filehandles * itself instead of letting the generic NFS code do it. This * allows filesystems with 64 bit inode numbers to be exported. * * Note that a side effect is that xfs_vget() won't be passed a * zero inode/generation pair under normal circumstances. As * however a malicious client could send us such data, the check * remains in that code. */ STATIC struct dentry * linvfs_decode_fh( struct super_block *sb, __u32 *fh, int fh_len, int fileid_type, int (*acceptable)( void *context, struct dentry *de), void *context) { xfs_fid2_t ifid; xfs_fid2_t pfid; void *parent = NULL; int is64 = 0; __u32 *p = fh; #if XFS_BIG_INUMS is64 = (fileid_type & XFS_FILEID_TYPE_64FLAG); fileid_type &= ~XFS_FILEID_TYPE_64FLAG; #endif /* * Note that we only accept fileids which are long enough * rather than allow the parent generation number to default * to zero. XFS considers zero a valid generation number not * an invalid/wildcard value. There's little point printk'ing * a warning here as we don't have the client information * which would make such a warning useful. */ if (fileid_type > 2 || fh_len < xfs_fileid_length((fileid_type == 2), is64)) return NULL; p = xfs_fileid_decode_fid2(p, &ifid, is64); if (fileid_type == 2) { p = xfs_fileid_decode_fid2(p, &pfid, is64); parent = &pfid; } fh = (__u32 *)&ifid; return sb->s_export_op->find_exported_dentry(sb, fh, parent, acceptable, context); } STATIC int linvfs_encode_fh( struct dentry *dentry, __u32 *fh, int *max_len, int connectable) { struct inode *inode = dentry->d_inode; int type = 1; __u32 *p = fh; int len; int is64 = 0; #if XFS_BIG_INUMS vfs_t *vfs = LINVFS_GET_VFS(inode->i_sb); if (!(vfs->vfs_flag & VFS_32BITINODES)) { /* filesystem may contain 64bit inode numbers */ is64 = XFS_FILEID_TYPE_64FLAG; } #endif /* Directories don't need their parent encoded, they have ".." */ if (S_ISDIR(inode->i_mode)) connectable = 0; /* * Only encode if there is enough space given. In practice * this means we can't export a filesystem with 64bit inodes * over NFSv2 with the subtree_check export option; the other * seven combinations work. The real answer is "don't use v2". */ len = xfs_fileid_length(connectable, is64); if (*max_len < len) return 255; *max_len = len; p = xfs_fileid_encode_inode(p, inode, is64); if (connectable) { spin_lock(&dentry->d_lock); p = xfs_fileid_encode_inode(p, dentry->d_parent->d_inode, is64); spin_unlock(&dentry->d_lock); type = 2; } BUG_ON((p - fh) != len); return type | is64; } STATIC struct dentry * linvfs_get_dentry( struct super_block *sb, void *data) { vnode_t *vp; struct inode *inode; struct dentry *result; vfs_t *vfsp = LINVFS_GET_VFS(sb); int error; VFS_VGET(vfsp, &vp, (fid_t *)data, error); if (error || vp == NULL) return ERR_PTR(-ESTALE) ; inode = LINVFS_GET_IP(vp); result = d_alloc_anon(inode); if (!result) { iput(inode); return ERR_PTR(-ENOMEM); } return result; } STATIC struct dentry * linvfs_get_parent( struct dentry *child) { int error; vnode_t *vp, *cvp; struct dentry *parent; struct dentry dotdot; dotdot.d_name.name = ".."; dotdot.d_name.len = 2; dotdot.d_inode = NULL; cvp = NULL; vp = LINVFS_GET_VP(child->d_inode); VOP_LOOKUP(vp, &dotdot, &cvp, 0, NULL, NULL, error); if (unlikely(error)) return ERR_PTR(-error); parent = d_alloc_anon(LINVFS_GET_IP(cvp)); if (unlikely(!parent)) { VN_RELE(cvp); return ERR_PTR(-ENOMEM); } return parent; } struct export_operations linvfs_export_ops = { .decode_fh = linvfs_decode_fh, .encode_fh = linvfs_encode_fh, .get_parent = linvfs_get_parent, .get_dentry = linvfs_get_dentry, };