Commit c38344fe authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Linus Torvalds

xfs: new export ops

This one is a lot more complicated than the previous ones.  XFS already had a
very clever scheme for supporting 64bit inode numbers in filehandles, and I've
reworked this to be some kind of a prototype for the generic 64bit inode
filehandle support.
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Cc: Neil Brown <neilb@suse.de>
Cc: "J. Bruce Fields" <bfields@fieldses.org>
Cc: David Chinner <dgc@sgi.com>
Cc: Timothy Shimmin <tes@sgi.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent a3513206
......@@ -33,62 +33,25 @@
static struct dentry dotdot = { .d_name.name = "..", .d_name.len = 2, };
/*
* 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.
* 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.
*/
STATIC struct dentry *
xfs_fs_decode_fh(
struct super_block *sb,
__u32 *fh,
int fh_len,
int fileid_type,
int (*acceptable)(
void *context,
struct dentry *de),
void *context)
static int xfs_fileid_length(int fileid_type)
{
xfs_fid_t ifid;
xfs_fid_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;
switch (fileid_type) {
case FILEID_INO32_GEN:
return 2;
case FILEID_INO32_GEN_PARENT:
return 4;
case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG:
return 3;
case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG:
return 6;
}
fh = (__u32 *)&ifid;
return sb->s_export_op->find_exported_dentry(sb, fh, parent, acceptable, context);
return 255; /* invalid */
}
STATIC int
xfs_fs_encode_fh(
struct dentry *dentry,
......@@ -96,21 +59,21 @@ xfs_fs_encode_fh(
int *max_len,
int connectable)
{
struct fid *fid = (struct fid *)fh;
struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fh;
struct inode *inode = dentry->d_inode;
int type = 1;
__u32 *p = fh;
int fileid_type;
int len;
int is64 = 0;
#if XFS_BIG_INUMS
if (!(XFS_M(inode->i_sb)->m_flags & XFS_MOUNT_SMALL_INUMS)) {
/* 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;
fileid_type = FILEID_INO32_GEN;
else
fileid_type = FILEID_INO32_GEN_PARENT;
/* filesystem may contain 64bit inode numbers */
if (!(XFS_M(inode->i_sb)->m_flags & XFS_MOUNT_SMALL_INUMS))
fileid_type |= XFS_FILEID_TYPE_64FLAG;
/*
* Only encode if there is enough space given. In practice
......@@ -118,39 +81,118 @@ xfs_fs_encode_fh(
* 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);
len = xfs_fileid_length(fileid_type);
if (*max_len < len)
return 255;
*max_len = len;
p = xfs_fileid_encode_inode(p, inode, is64);
if (connectable) {
switch (fileid_type) {
case FILEID_INO32_GEN_PARENT:
spin_lock(&dentry->d_lock);
p = xfs_fileid_encode_inode(p, dentry->d_parent->d_inode, is64);
fid->i32.parent_ino = dentry->d_parent->d_inode->i_ino;
fid->i32.parent_gen = dentry->d_parent->d_inode->i_generation;
spin_unlock(&dentry->d_lock);
type = 2;
/*FALLTHRU*/
case FILEID_INO32_GEN:
fid->i32.ino = inode->i_ino;
fid->i32.gen = inode->i_generation;
break;
case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG:
spin_lock(&dentry->d_lock);
fid64->parent_ino = dentry->d_parent->d_inode->i_ino;
fid64->parent_gen = dentry->d_parent->d_inode->i_generation;
spin_unlock(&dentry->d_lock);
/*FALLTHRU*/
case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG:
fid64->ino = inode->i_ino;
fid64->gen = inode->i_generation;
break;
}
BUG_ON((p - fh) != len);
return type | is64;
return fileid_type;
}
STATIC struct dentry *
xfs_fs_get_dentry(
STATIC struct inode *
xfs_nfs_get_inode(
struct super_block *sb,
void *data)
{
u64 ino,
u32 generation)
{
xfs_fid_t xfid;
bhv_vnode_t *vp;
struct inode *inode;
struct dentry *result;
int error;
error = xfs_vget(XFS_M(sb), &vp, data);
if (error || vp == NULL)
return ERR_PTR(-ESTALE) ;
xfid.fid_len = sizeof(xfs_fid_t) - sizeof(xfid.fid_len);
xfid.fid_pad = 0;
xfid.fid_ino = ino;
xfid.fid_gen = generation;
inode = vn_to_inode(vp);
error = xfs_vget(XFS_M(sb), &vp, &xfid);
if (error)
return ERR_PTR(-error);
return vp ? vn_to_inode(vp) : NULL;
}
STATIC struct dentry *
xfs_fs_fh_to_dentry(struct super_block *sb, struct fid *fid,
int fh_len, int fileid_type)
{
struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fid;
struct inode *inode = NULL;
struct dentry *result;
if (fh_len < xfs_fileid_length(fileid_type))
return NULL;
switch (fileid_type) {
case FILEID_INO32_GEN_PARENT:
case FILEID_INO32_GEN:
inode = xfs_nfs_get_inode(sb, fid->i32.ino, fid->i32.gen);
break;
case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG:
case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG:
inode = xfs_nfs_get_inode(sb, fid64->ino, fid64->gen);
break;
}
if (!inode)
return NULL;
if (IS_ERR(inode))
return ERR_PTR(PTR_ERR(inode));
result = d_alloc_anon(inode);
if (!result) {
iput(inode);
return ERR_PTR(-ENOMEM);
}
return result;
}
STATIC struct dentry *
xfs_fs_fh_to_parent(struct super_block *sb, struct fid *fid,
int fh_len, int fileid_type)
{
struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fid;
struct inode *inode = NULL;
struct dentry *result;
switch (fileid_type) {
case FILEID_INO32_GEN_PARENT:
inode = xfs_nfs_get_inode(sb, fid->i32.parent_ino,
fid->i32.parent_gen);
break;
case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG:
inode = xfs_nfs_get_inode(sb, fid64->parent_ino,
fid64->parent_gen);
break;
}
if (!inode)
return NULL;
if (IS_ERR(inode))
return ERR_PTR(PTR_ERR(inode));
result = d_alloc_anon(inode);
if (!result) {
if (!result) {
iput(inode);
return ERR_PTR(-ENOMEM);
}
......@@ -179,8 +221,8 @@ xfs_fs_get_parent(
}
struct export_operations xfs_export_operations = {
.decode_fh = xfs_fs_decode_fh,
.encode_fh = xfs_fs_encode_fh,
.fh_to_dentry = xfs_fs_fh_to_dentry,
.fh_to_parent = xfs_fs_fh_to_parent,
.get_parent = xfs_fs_get_parent,
.get_dentry = xfs_fs_get_dentry,
};
......@@ -59,50 +59,14 @@
* a subdirectory) or use the "fsid" export option.
*/
struct xfs_fid64 {
u64 ino;
u32 gen;
u64 parent_ino;
u32 parent_gen;
} __attribute__((packed));
/* This flag goes on the wire. Don't play with it. */
#define XFS_FILEID_TYPE_64FLAG 0x80 /* NFS fileid has 64bit inodes */
/* Calculate the length in u32 units of the fileid data */
static inline int
xfs_fileid_length(int hasparent, int is64)
{
return hasparent ? (is64 ? 6 : 4) : (is64 ? 3 : 2);
}
/*
* Decode encoded inode information (either for the inode itself
* or the parent) into an xfs_fid_t structure. Advances and
* returns the new data pointer
*/
static inline __u32 *
xfs_fileid_decode_fid2(__u32 *p, xfs_fid_t *fid, int is64)
{
fid->fid_len = sizeof(xfs_fid_t) - sizeof(fid->fid_len);
fid->fid_pad = 0;
fid->fid_ino = *p++;
#if XFS_BIG_INUMS
if (is64)
fid->fid_ino |= (((__u64)(*p++)) << 32);
#endif
fid->fid_gen = *p++;
return p;
}
/*
* Encode inode information (either for the inode itself or the
* parent) into a fileid buffer. Advances and returns the new
* data pointer.
*/
static inline __u32 *
xfs_fileid_encode_inode(__u32 *p, struct inode *inode, int is64)
{
*p++ = (__u32)inode->i_ino;
#if XFS_BIG_INUMS
if (is64)
*p++ = (__u32)(inode->i_ino >> 32);
#endif
*p++ = inode->i_generation;
return p;
}
#endif /* __XFS_EXPORT_H__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment