OpenCloudOS-Kernel/fs/xfs/linux-2.6/xfs_export.c

189 lines
4.6 KiB
C

/*
* 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);
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,
};