| /* |
| * 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 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); |
| xfs_mount_t *mp = XFS_VFSTOM(vfs); |
| |
| if (!(mp->m_flags & XFS_MOUNT_32BITINOOPT)) { |
| /* 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, |
| }; |